From 44d21fa8680924d6cc73e1ed138185481e52e806 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 7 Sep 2023 10:07:27 +0100 Subject: [PATCH 001/218] Make block provider stoppable --- src/main/java/org/peergos/EmbeddedIpfs.java | 6 ++++-- src/main/java/org/peergos/PeriodicBlockProvider.java | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 3f82b053..6298210c 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -37,6 +37,7 @@ public class EmbeddedIpfs { public final Bitswap bitswap; public final Optional p2pHttp; private final List bootstrap; + private final PeriodicBlockProvider blockProvider; public EmbeddedIpfs(Host node, ProvidingBlockstore blockstore, @@ -53,6 +54,8 @@ public EmbeddedIpfs(Host node, this.p2pHttp = p2pHttp; this.bootstrap = bootstrap; this.blocks = new BitswapBlockService(node, bitswap, dht); + this.blockProvider = new PeriodicBlockProvider(22 * 3600_000L, + () -> blockstore.refs().join().stream(), node, dht, blockstore.toPublish); } public List getBlocks(List wants, Set peers, boolean addToLocal) { @@ -88,13 +91,12 @@ public void start() { LOG.info("Bootstrapping IPFS kademlia"); dht.bootstrap(node); - PeriodicBlockProvider blockProvider = new PeriodicBlockProvider(22 * 3600_000L, - () -> blockstore.refs().join().stream(), node, dht, blockstore.toPublish); blockProvider.start(); } public CompletableFuture stop() throws Exception { records.close(); + blockProvider.stop(); return node.stop(); } diff --git a/src/main/java/org/peergos/PeriodicBlockProvider.java b/src/main/java/org/peergos/PeriodicBlockProvider.java index c132b6e0..84210b2f 100644 --- a/src/main/java/org/peergos/PeriodicBlockProvider.java +++ b/src/main/java/org/peergos/PeriodicBlockProvider.java @@ -6,6 +6,7 @@ import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.*; import java.util.function.*; import java.util.logging.*; import java.util.stream.*; @@ -32,13 +33,19 @@ public PeriodicBlockProvider(long reprovideIntervalMillis, this.newBlocksToPublish = newBlocksToPublish; } + private final AtomicBoolean running = new AtomicBoolean(false); public void start() { + running.set(true); new Thread(this::run, "CidReprovider").start(); new Thread(this::provideNewBlocks, "NewCidProvider").start(); } + public void stop() { + running.set(false); + } + public void run() { - while (true) { + while (running.get()) { try { publish(getBlocks.get()); Thread.sleep(reprovideIntervalMillis); @@ -49,7 +56,7 @@ public void run() { } public void provideNewBlocks() { - while (true) { + while (running.get()) { try { Cid c = newBlocksToPublish.poll(); if (c != null) { From 326ee2d90b6019841ee39d9d3d1d3584631f290e Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Thu, 14 Sep 2023 10:57:30 +0100 Subject: [PATCH 002/218] chat experiment --- examples/chat/Chat.java | 146 +++++++++++++++++ examples/chat/Chat2.java | 150 ++++++++++++++++++ src/main/java/org/peergos/EmbeddedIpfs.java | 8 +- src/main/java/org/peergos/Nabu.java | 6 +- src/main/java/org/peergos/net/Handler.java | 2 +- .../protocol/dht/DatabaseRecordStore.java | 2 +- .../peergos/protocol/dht/RamRecordStore.java | 3 + .../org/peergos/protocol/dht/RecordStore.java | 2 +- 8 files changed, 310 insertions(+), 9 deletions(-) create mode 100644 examples/chat/Chat.java create mode 100644 examples/chat/Chat2.java diff --git a/examples/chat/Chat.java b/examples/chat/Chat.java new file mode 100644 index 00000000..90c657e9 --- /dev/null +++ b/examples/chat/Chat.java @@ -0,0 +1,146 @@ +package chat; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import io.ipfs.cid.Cid; +import io.ipfs.multiaddr.MultiAddress; +import io.ipfs.multihash.Multihash; +import io.libp2p.core.AddressBook; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.multiformats.Multiaddr; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.*; +import org.peergos.*; +import org.peergos.blockstore.*; +import org.peergos.config.*; +import org.peergos.net.Handler; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.RamRecordStore; +import org.peergos.protocol.dht.RecordStore; +import org.peergos.protocol.http.HttpProtocol; +import org.peergos.util.HttpUtil; +import org.peergos.util.Version; + +import java.io.Console; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.logging.Logger; + +public class Chat { + + private EmbeddedIpfs embeddedIpfs; + + public static final Version CURRENT_VERSION = Version.parse("0.0.1"); + + private static final Logger LOG = Logger.getGlobal(); + + private static HttpProtocol.HttpRequestProcessor proxyHandler(MultiAddress target) { + return (s, req, h) -> HttpProtocol.proxyRequest(req, new InetSocketAddress(target.getHost(), target.getPort()), h); + } + + public Chat(int portNumber, Multihash targetAddress) throws IOException { + RecordStore recordStore = new RamRecordStore(); + Blockstore blockStore = new RamBlockstore(); + + LOG.info("Starting Chat version: " + CURRENT_VERSION); + + List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/" + portNumber)); + List bootstrapNodes = new ArrayList<>(Config.defaultBootstrapNodes); + + HostBuilder builder = new HostBuilder().generateIdentity(); + PrivKey privKey = builder.getPrivateKey(); + PeerId peerId = builder.getPeerId(); + + IdentitySection identitySection = new IdentitySection(privKey.bytes(), peerId); + BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); + + MultiAddress proxyTargetAddress = new MultiAddress("/ip4/127.0.0.1/tcp/8003"); + embeddedIpfs = EmbeddedIpfs.build(recordStore, blockStore, + swarmAddresses, + bootstrapNodes, + identitySection, + authoriser, Optional.of(Chat.proxyHandler(proxyTargetAddress))); + embeddedIpfs.start(); + + int apiPort = 10000 + new Random().nextInt(50000); + MultiAddress apiAddress = new MultiAddress("/ip4/127.0.0.1/tcp/" + apiPort); + InetSocketAddress localAPIAddress = new InetSocketAddress(apiAddress.getHost(), apiAddress.getPort()); + + LOG.info("Starting RPC API server at " + apiAddress.getHost() + ":" + localAPIAddress.getPort()); + HttpServer apiServer = HttpServer.create(localAPIAddress, 500); + + apiServer.createContext(HttpProxyService.API_URL, new Handler() { + @Override + public void handleCallToAPI(HttpExchange httpExchange) { + String path = httpExchange.getRequestURI().getPath(); + System.out.println("PROXY PATH=" + path); + try { + httpExchange.sendResponseHeaders(200, 0); + httpExchange.getResponseBody().flush(); + } catch (Exception ex) { + HttpUtil.replyError(httpExchange, ex); + } finally { + httpExchange.close(); + } + } + }); + apiServer.setExecutor(Executors.newFixedThreadPool(50)); + apiServer.start(); + + Thread shutdownHook = new Thread(() -> { + LOG.info("Stopping server..."); + try { + embeddedIpfs.stop().join(); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + Runtime.getRuntime().addShutdownHook(shutdownHook); + //runChat(embeddedIpfs.node, embeddedIpfs.dht, embeddedIpfs.p2pHttp.get(), targetAddress); + } + private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBinding, Multihash targetNodeId) { + + AddressBook addressBook = node.getAddressBook(); + PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); + Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); + Multiaddr[] allAddresses = null; + if (targetAddressesOpt.isEmpty()) { + List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); + Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); + if (matching.isEmpty()) { + throw new IllegalStateException("Target not found: " + targetNodeId); + } + PeerAddresses peer = matching.get(); + allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); + } + Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? + Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) + : allAddresses; + byte[] msg = "world!".getBytes(); + HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, + HttpMethod.GET, "/p2p/hello", Unpooled.wrappedBuffer(msg)); + + HttpHeaders reqHeaders = httpRequest.headers(); + reqHeaders.set(HttpHeaderNames.CONTENT_LENGTH, msg.length); + FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); + int code = resp.status().code(); + resp.release(); + System.currentTimeMillis(); + } + + public static void main(String[] args) throws IOException { + Console console = System.console(); + System.out.println("Enter swam port"); + Integer portNumber = Integer.parseInt("8123");//console.readLine().trim()); + System.out.println("Enter PeerId of other node"); + Multihash targetNodeId = Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg");//console.readLine().trim()); + new Chat(portNumber, targetNodeId); + } + +} diff --git a/examples/chat/Chat2.java b/examples/chat/Chat2.java new file mode 100644 index 00000000..ef3b883e --- /dev/null +++ b/examples/chat/Chat2.java @@ -0,0 +1,150 @@ +package chat; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import io.ipfs.cid.Cid; +import io.ipfs.multiaddr.MultiAddress; +import io.ipfs.multihash.Multihash; +import io.libp2p.core.AddressBook; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.multiformats.Multiaddr; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.*; +import org.peergos.*; +import org.peergos.blockstore.Blockstore; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.config.Config; +import org.peergos.config.IdentitySection; +import org.peergos.net.Handler; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.RamRecordStore; +import org.peergos.protocol.dht.RecordStore; +import org.peergos.protocol.http.HttpProtocol; +import org.peergos.util.HttpUtil; +import org.peergos.util.Version; + +import java.io.Console; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.logging.Logger; + +public class Chat2 { + + private EmbeddedIpfs embeddedIpfs; + + public static final Version CURRENT_VERSION = Version.parse("0.0.1"); + + private static final Logger LOG = Logger.getGlobal(); + + private static HttpProtocol.HttpRequestProcessor proxyHandler(MultiAddress target) { + return (s, req, h) -> HttpProtocol.proxyRequest(req, new InetSocketAddress(target.getHost(), target.getPort()), h); + } + + public Chat2(int portNumber, Multihash targetAddress) throws IOException { + RecordStore recordStore = new RamRecordStore(); + Blockstore blockStore = new RamBlockstore(); + + LOG.info("Starting Chat version: " + CURRENT_VERSION); + + List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/" + portNumber)); + List bootstrapNodes = new ArrayList<>(Config.defaultBootstrapNodes); + + HostBuilder builder = new HostBuilder().generateIdentity(); + PrivKey privKey = builder.getPrivateKey(); + PeerId peerId = builder.getPeerId(); + + IdentitySection identitySection = new IdentitySection(privKey.bytes(), peerId); + BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); + + MultiAddress proxyTargetAddress = new MultiAddress("/ip4/127.0.0.1/tcp/8003"); + embeddedIpfs = EmbeddedIpfs.build(recordStore, blockStore, + swarmAddresses, + bootstrapNodes, + identitySection, + authoriser, Optional.of(Chat2.proxyHandler(proxyTargetAddress))); + embeddedIpfs.start(); + + int apiPort = 10000 + new Random().nextInt(50000); + MultiAddress apiAddress = new MultiAddress("/ip4/127.0.0.1/tcp/" + apiPort); + InetSocketAddress localAPIAddress = new InetSocketAddress(apiAddress.getHost(), apiAddress.getPort()); + + LOG.info("Starting RPC API server at " + apiAddress.getHost() + ":" + localAPIAddress.getPort()); + HttpServer apiServer = HttpServer.create(localAPIAddress, 500); + + apiServer.createContext(HttpProxyService.API_URL, new Handler() { + @Override + public void handleCallToAPI(HttpExchange httpExchange) { + String path = httpExchange.getRequestURI().getPath(); + System.out.println("PROXY PATH=" + path); + try { + httpExchange.sendResponseHeaders(200, 0); + httpExchange.getResponseBody().flush(); + } catch (Exception ex) { + HttpUtil.replyError(httpExchange, ex); + } finally { + httpExchange.close(); + } + } + }); + apiServer.setExecutor(Executors.newFixedThreadPool(50)); + apiServer.start(); + + Thread shutdownHook = new Thread(() -> { + LOG.info("Stopping server..."); + try { + embeddedIpfs.stop().join(); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + Runtime.getRuntime().addShutdownHook(shutdownHook); + runChat(embeddedIpfs.node, embeddedIpfs.dht, embeddedIpfs.p2pHttp.get(), targetAddress); + } + private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBinding, Multihash targetNodeId) { + + AddressBook addressBook = node.getAddressBook(); + PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); + Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); + Multiaddr[] allAddresses = null; + if (targetAddressesOpt.isEmpty()) { + List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); + Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); + if (matching.isEmpty()) { + throw new IllegalStateException("Target not found: " + targetNodeId); + } + PeerAddresses peer = matching.get(); + allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); + } + Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? + Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) + : allAddresses; + byte[] msg = "world!".getBytes(); + HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, + HttpMethod.GET, "/p2p/hello", Unpooled.wrappedBuffer(msg)); + + HttpHeaders reqHeaders = httpRequest.headers(); + reqHeaders.set(HttpHeaderNames.CONTENT_LENGTH, msg.length); + FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); + int code = resp.status().code(); + resp.release(); + System.currentTimeMillis(); + } + + public static void main(String[] args) throws IOException { + Console console = System.console(); + System.out.println("Enter swam port"); + Integer portNumber = Integer.parseInt("8456");//console.readLine().trim()); + System.out.println("Enter PeerId of other node"); + String peerId = "12D3KooWDiy5Ng3TXJV1n5tNr11uTXDeV4EZVgUdcPkeSQFAjpUp"; + Multihash targetNodeId = Multihash.deserialize(PeerId.fromBase58(peerId).getBytes()); + //Multihash targetNodeId = Cid.decode("12D3KooWB3gqneGv4NMeJbqxYscpZGBLoC4TPDS6P3PWXbtq3Ubh");//console.readLine().trim()); + new Chat2(portNumber, targetNodeId); + } + +} diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 6298210c..51368025 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -31,7 +31,7 @@ public class EmbeddedIpfs { public final Host node; public final ProvidingBlockstore blockstore; public final BlockService blocks; - public final DatabaseRecordStore records; + public final RecordStore records; public final Kademlia dht; public final Bitswap bitswap; @@ -41,7 +41,7 @@ public class EmbeddedIpfs { public EmbeddedIpfs(Host node, ProvidingBlockstore blockstore, - DatabaseRecordStore records, + RecordStore records, Kademlia dht, Bitswap bitswap, Optional p2pHttp, @@ -123,7 +123,7 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath) { blockStore : new TypeLimitedBlockstore(blockStore, config.datastore.allowedCodecs.codecs); } - public static EmbeddedIpfs build(Path ipfsPath, + public static EmbeddedIpfs build(RecordStore records, Blockstore blocks, List swarmAddresses, List bootstrap, @@ -131,8 +131,6 @@ public static EmbeddedIpfs build(Path ipfsPath, BlockRequestAuthoriser authoriser, Optional handler) { ProvidingBlockstore blockstore = new ProvidingBlockstore(blocks); - Path datastorePath = ipfsPath.resolve("datastore").resolve("h2.datastore"); - DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toString()); ProviderStore providers = new RamProviderStore(); HostBuilder builder = new HostBuilder().setIdentity(identity.privKeyProtobuf).listen(swarmAddresses); diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 77ba3123..fc5b9aaf 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -7,6 +7,7 @@ import org.peergos.config.*; import org.peergos.net.APIHandler; import org.peergos.net.HttpProxyHandler; +import org.peergos.protocol.dht.DatabaseRecordStore; import org.peergos.protocol.http.*; import org.peergos.util.HttpUtil; import org.peergos.util.JSONParser; @@ -44,7 +45,10 @@ public Nabu(Args args) throws Exception { LOG.info("Starting Nabu version: " + APIHandler.CURRENT_VERSION); BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); - EmbeddedIpfs ipfs = EmbeddedIpfs.build(ipfsPath, + Path datastorePath = ipfsPath.resolve("datastore").resolve("h2.datastore"); + DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toString()); + + EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, buildBlockStore(config, ipfsPath), config.addresses.getSwarmAddresses(), config.bootstrap.getBootstrapAddresses(), diff --git a/src/main/java/org/peergos/net/Handler.java b/src/main/java/org/peergos/net/Handler.java index 4797ad6b..c372d731 100755 --- a/src/main/java/org/peergos/net/Handler.java +++ b/src/main/java/org/peergos/net/Handler.java @@ -19,7 +19,7 @@ public Handler() { } - abstract void handleCallToAPI(HttpExchange httpExchange); + public abstract void handleCallToAPI(HttpExchange httpExchange); @Override public void handle(HttpExchange httpExchange) { diff --git a/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java b/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java index f2819e50..b5e65930 100644 --- a/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java +++ b/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java @@ -12,7 +12,7 @@ import java.time.ZoneOffset; import java.util.Optional; -public class DatabaseRecordStore implements RecordStore, AutoCloseable { +public class DatabaseRecordStore implements RecordStore { private final String connectionStringPrefix = "jdbc:h2:";//./store/records;AUTO_RECONNECT=TRUE private final Connection connection; diff --git a/src/main/java/org/peergos/protocol/dht/RamRecordStore.java b/src/main/java/org/peergos/protocol/dht/RamRecordStore.java index eb68a1d8..7ead53c2 100644 --- a/src/main/java/org/peergos/protocol/dht/RamRecordStore.java +++ b/src/main/java/org/peergos/protocol/dht/RamRecordStore.java @@ -27,4 +27,7 @@ public Optional get(Cid peerId) { public void remove(Multihash peerId) { records.remove(peerId); } + + @Override + public void close() throws Exception {} } diff --git a/src/main/java/org/peergos/protocol/dht/RecordStore.java b/src/main/java/org/peergos/protocol/dht/RecordStore.java index 9a5c278c..1a1229b9 100644 --- a/src/main/java/org/peergos/protocol/dht/RecordStore.java +++ b/src/main/java/org/peergos/protocol/dht/RecordStore.java @@ -6,7 +6,7 @@ import java.util.*; -public interface RecordStore { +public interface RecordStore extends AutoCloseable { void put(Multihash peerId, IpnsRecord record); From 0750cce1f0e9dc8918f03172ca1014041f388648 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Thu, 14 Sep 2023 14:25:34 +0100 Subject: [PATCH 003/218] simple version --- examples/chat/Chat.java | 163 ++++++++++++--------------------------- examples/chat/Chat2.java | 150 ----------------------------------- 2 files changed, 48 insertions(+), 265 deletions(-) delete mode 100644 examples/chat/Chat2.java diff --git a/examples/chat/Chat.java b/examples/chat/Chat.java index 90c657e9..eb7d44d4 100644 --- a/examples/chat/Chat.java +++ b/examples/chat/Chat.java @@ -1,146 +1,79 @@ package chat; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpServer; -import io.ipfs.cid.Cid; -import io.ipfs.multiaddr.MultiAddress; -import io.ipfs.multihash.Multihash; -import io.libp2p.core.AddressBook; import io.libp2p.core.Host; -import io.libp2p.core.PeerId; -import io.libp2p.core.crypto.PrivKey; import io.libp2p.core.multiformats.Multiaddr; +import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.*; -import org.peergos.*; -import org.peergos.blockstore.*; -import org.peergos.config.*; -import org.peergos.net.Handler; -import org.peergos.protocol.dht.Kademlia; +import org.peergos.HostBuilder; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.protocol.dht.RamProviderStore; import org.peergos.protocol.dht.RamRecordStore; -import org.peergos.protocol.dht.RecordStore; import org.peergos.protocol.http.HttpProtocol; -import org.peergos.util.HttpUtil; -import org.peergos.util.Version; -import java.io.Console; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.*; +import java.nio.charset.Charset; +import java.util.Random; +import java.util.Scanner; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; -import java.util.logging.Logger; public class Chat { - private EmbeddedIpfs embeddedIpfs; + public Chat() { - public static final Version CURRENT_VERSION = Version.parse("0.0.1"); + FullHttpResponse replyOk = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.buffer(0)); + replyOk.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); - private static final Logger LOG = Logger.getGlobal(); - - private static HttpProtocol.HttpRequestProcessor proxyHandler(MultiAddress target) { - return (s, req, h) -> HttpProtocol.proxyRequest(req, new InetSocketAddress(target.getHost(), target.getPort()), h); - } - - public Chat(int portNumber, Multihash targetAddress) throws IOException { - RecordStore recordStore = new RamRecordStore(); - Blockstore blockStore = new RamBlockstore(); - - LOG.info("Starting Chat version: " + CURRENT_VERSION); - - List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/" + portNumber)); - List bootstrapNodes = new ArrayList<>(Config.defaultBootstrapNodes); - - HostBuilder builder = new HostBuilder().generateIdentity(); - PrivKey privKey = builder.getPrivateKey(); - PeerId peerId = builder.getPeerId(); - - IdentitySection identitySection = new IdentitySection(privKey.bytes(), peerId); - BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); - - MultiAddress proxyTargetAddress = new MultiAddress("/ip4/127.0.0.1/tcp/8003"); - embeddedIpfs = EmbeddedIpfs.build(recordStore, blockStore, - swarmAddresses, - bootstrapNodes, - identitySection, - authoriser, Optional.of(Chat.proxyHandler(proxyTargetAddress))); - embeddedIpfs.start(); - - int apiPort = 10000 + new Random().nextInt(50000); - MultiAddress apiAddress = new MultiAddress("/ip4/127.0.0.1/tcp/" + apiPort); - InetSocketAddress localAPIAddress = new InetSocketAddress(apiAddress.getHost(), apiAddress.getPort()); - - LOG.info("Starting RPC API server at " + apiAddress.getHost() + ":" + localAPIAddress.getPort()); - HttpServer apiServer = HttpServer.create(localAPIAddress, 500); - - apiServer.createContext(HttpProxyService.API_URL, new Handler() { - @Override - public void handleCallToAPI(HttpExchange httpExchange) { - String path = httpExchange.getRequestURI().getPath(); - System.out.println("PROXY PATH=" + path); - try { - httpExchange.sendResponseHeaders(200, 0); - httpExchange.getResponseBody().flush(); - } catch (Exception ex) { - HttpUtil.replyError(httpExchange, ex); - } finally { - httpExchange.close(); - } + HttpProtocol.Binding node1Http = new HttpProtocol.Binding((s, req, h) -> { + if (req instanceof FullHttpRequest) { + ByteBuf content = ((FullHttpRequest) req).content(); + CharSequence contents = content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset()); + String output = contents.toString(); + System.out.println("received msg:" + output); } + h.accept(replyOk.retain()); }); - apiServer.setExecutor(Executors.newFixedThreadPool(50)); - apiServer.start(); + int portNumber = 10000 + new Random().nextInt(50000); + + HostBuilder builder = HostBuilder.create(portNumber, + new RamProviderStore(), + new RamRecordStore(), + new RamBlockstore(), + (c, b, p, a) -> CompletableFuture.completedFuture(true)) + .addProtocol(node1Http); + + Host node = builder.build(); + node.start().join(); Thread shutdownHook = new Thread(() -> { - LOG.info("Stopping server..."); try { - embeddedIpfs.stop().join(); + node.stop(); } catch (Exception ex) { ex.printStackTrace(); } }); Runtime.getRuntime().addShutdownHook(shutdownHook); - //runChat(embeddedIpfs.node, embeddedIpfs.dht, embeddedIpfs.p2pHttp.get(), targetAddress); - } - private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBinding, Multihash targetNodeId) { - AddressBook addressBook = node.getAddressBook(); - PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); - Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); - Multiaddr[] allAddresses = null; - if (targetAddressesOpt.isEmpty()) { - List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); - Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); - if (matching.isEmpty()) { - throw new IllegalStateException("Target not found: " + targetNodeId); + Multiaddr address1 = node.listenAddresses().get(0); + System.out.println("My address=" + address1); + + System.out.println("Enter address of other node:"); + Scanner in = new Scanner(System.in); + String address2 = in.nextLine(); + if (address2.length() == 0) { + System.err.println("Invalid address"); + } else { + Multiaddr targetAddress = new Multiaddr(address2); + while (true) { + System.out.println("Enter msg:"); + byte[] msg = in.nextLine().getBytes(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg)); + httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg.length); + HttpProtocol.HttpController proxier = node1Http.dial(node, targetAddress).getController().join(); + proxier.send(httpRequest.retain()).join().release(); } - PeerAddresses peer = matching.get(); - allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); } - Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? - Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) - : allAddresses; - byte[] msg = "world!".getBytes(); - HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "/p2p/hello", Unpooled.wrappedBuffer(msg)); - - HttpHeaders reqHeaders = httpRequest.headers(); - reqHeaders.set(HttpHeaderNames.CONTENT_LENGTH, msg.length); - FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); - int code = resp.status().code(); - resp.release(); - System.currentTimeMillis(); } - - public static void main(String[] args) throws IOException { - Console console = System.console(); - System.out.println("Enter swam port"); - Integer portNumber = Integer.parseInt("8123");//console.readLine().trim()); - System.out.println("Enter PeerId of other node"); - Multihash targetNodeId = Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg");//console.readLine().trim()); - new Chat(portNumber, targetNodeId); + public static void main(String[] args) { + new Chat(); } - } diff --git a/examples/chat/Chat2.java b/examples/chat/Chat2.java deleted file mode 100644 index ef3b883e..00000000 --- a/examples/chat/Chat2.java +++ /dev/null @@ -1,150 +0,0 @@ -package chat; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpServer; -import io.ipfs.cid.Cid; -import io.ipfs.multiaddr.MultiAddress; -import io.ipfs.multihash.Multihash; -import io.libp2p.core.AddressBook; -import io.libp2p.core.Host; -import io.libp2p.core.PeerId; -import io.libp2p.core.crypto.PrivKey; -import io.libp2p.core.multiformats.Multiaddr; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.*; -import org.peergos.*; -import org.peergos.blockstore.Blockstore; -import org.peergos.blockstore.RamBlockstore; -import org.peergos.config.Config; -import org.peergos.config.IdentitySection; -import org.peergos.net.Handler; -import org.peergos.protocol.dht.Kademlia; -import org.peergos.protocol.dht.RamRecordStore; -import org.peergos.protocol.dht.RecordStore; -import org.peergos.protocol.http.HttpProtocol; -import org.peergos.util.HttpUtil; -import org.peergos.util.Version; - -import java.io.Console; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; -import java.util.logging.Logger; - -public class Chat2 { - - private EmbeddedIpfs embeddedIpfs; - - public static final Version CURRENT_VERSION = Version.parse("0.0.1"); - - private static final Logger LOG = Logger.getGlobal(); - - private static HttpProtocol.HttpRequestProcessor proxyHandler(MultiAddress target) { - return (s, req, h) -> HttpProtocol.proxyRequest(req, new InetSocketAddress(target.getHost(), target.getPort()), h); - } - - public Chat2(int portNumber, Multihash targetAddress) throws IOException { - RecordStore recordStore = new RamRecordStore(); - Blockstore blockStore = new RamBlockstore(); - - LOG.info("Starting Chat version: " + CURRENT_VERSION); - - List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/" + portNumber)); - List bootstrapNodes = new ArrayList<>(Config.defaultBootstrapNodes); - - HostBuilder builder = new HostBuilder().generateIdentity(); - PrivKey privKey = builder.getPrivateKey(); - PeerId peerId = builder.getPeerId(); - - IdentitySection identitySection = new IdentitySection(privKey.bytes(), peerId); - BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); - - MultiAddress proxyTargetAddress = new MultiAddress("/ip4/127.0.0.1/tcp/8003"); - embeddedIpfs = EmbeddedIpfs.build(recordStore, blockStore, - swarmAddresses, - bootstrapNodes, - identitySection, - authoriser, Optional.of(Chat2.proxyHandler(proxyTargetAddress))); - embeddedIpfs.start(); - - int apiPort = 10000 + new Random().nextInt(50000); - MultiAddress apiAddress = new MultiAddress("/ip4/127.0.0.1/tcp/" + apiPort); - InetSocketAddress localAPIAddress = new InetSocketAddress(apiAddress.getHost(), apiAddress.getPort()); - - LOG.info("Starting RPC API server at " + apiAddress.getHost() + ":" + localAPIAddress.getPort()); - HttpServer apiServer = HttpServer.create(localAPIAddress, 500); - - apiServer.createContext(HttpProxyService.API_URL, new Handler() { - @Override - public void handleCallToAPI(HttpExchange httpExchange) { - String path = httpExchange.getRequestURI().getPath(); - System.out.println("PROXY PATH=" + path); - try { - httpExchange.sendResponseHeaders(200, 0); - httpExchange.getResponseBody().flush(); - } catch (Exception ex) { - HttpUtil.replyError(httpExchange, ex); - } finally { - httpExchange.close(); - } - } - }); - apiServer.setExecutor(Executors.newFixedThreadPool(50)); - apiServer.start(); - - Thread shutdownHook = new Thread(() -> { - LOG.info("Stopping server..."); - try { - embeddedIpfs.stop().join(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - Runtime.getRuntime().addShutdownHook(shutdownHook); - runChat(embeddedIpfs.node, embeddedIpfs.dht, embeddedIpfs.p2pHttp.get(), targetAddress); - } - private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBinding, Multihash targetNodeId) { - - AddressBook addressBook = node.getAddressBook(); - PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); - Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); - Multiaddr[] allAddresses = null; - if (targetAddressesOpt.isEmpty()) { - List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); - Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); - if (matching.isEmpty()) { - throw new IllegalStateException("Target not found: " + targetNodeId); - } - PeerAddresses peer = matching.get(); - allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); - } - Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? - Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) - : allAddresses; - byte[] msg = "world!".getBytes(); - HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "/p2p/hello", Unpooled.wrappedBuffer(msg)); - - HttpHeaders reqHeaders = httpRequest.headers(); - reqHeaders.set(HttpHeaderNames.CONTENT_LENGTH, msg.length); - FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); - int code = resp.status().code(); - resp.release(); - System.currentTimeMillis(); - } - - public static void main(String[] args) throws IOException { - Console console = System.console(); - System.out.println("Enter swam port"); - Integer portNumber = Integer.parseInt("8456");//console.readLine().trim()); - System.out.println("Enter PeerId of other node"); - String peerId = "12D3KooWDiy5Ng3TXJV1n5tNr11uTXDeV4EZVgUdcPkeSQFAjpUp"; - Multihash targetNodeId = Multihash.deserialize(PeerId.fromBase58(peerId).getBytes()); - //Multihash targetNodeId = Cid.decode("12D3KooWB3gqneGv4NMeJbqxYscpZGBLoC4TPDS6P3PWXbtq3Ubh");//console.readLine().trim()); - new Chat2(portNumber, targetNodeId); - } - -} From 392d11e41e6eb74b33fd3b71b2976b9b4ebc47ae Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Thu, 14 Sep 2023 14:27:52 +0100 Subject: [PATCH 004/218] refactor EmbeddedIPFS --- examples/chat/Chat.java | 79 ----------------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 examples/chat/Chat.java diff --git a/examples/chat/Chat.java b/examples/chat/Chat.java deleted file mode 100644 index eb7d44d4..00000000 --- a/examples/chat/Chat.java +++ /dev/null @@ -1,79 +0,0 @@ -package chat; - -import io.libp2p.core.Host; -import io.libp2p.core.multiformats.Multiaddr; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.*; -import org.peergos.HostBuilder; -import org.peergos.blockstore.RamBlockstore; -import org.peergos.protocol.dht.RamProviderStore; -import org.peergos.protocol.dht.RamRecordStore; -import org.peergos.protocol.http.HttpProtocol; - -import java.nio.charset.Charset; -import java.util.Random; -import java.util.Scanner; -import java.util.concurrent.CompletableFuture; - -public class Chat { - - public Chat() { - - FullHttpResponse replyOk = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.buffer(0)); - replyOk.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); - - HttpProtocol.Binding node1Http = new HttpProtocol.Binding((s, req, h) -> { - if (req instanceof FullHttpRequest) { - ByteBuf content = ((FullHttpRequest) req).content(); - CharSequence contents = content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset()); - String output = contents.toString(); - System.out.println("received msg:" + output); - } - h.accept(replyOk.retain()); - }); - - int portNumber = 10000 + new Random().nextInt(50000); - - HostBuilder builder = HostBuilder.create(portNumber, - new RamProviderStore(), - new RamRecordStore(), - new RamBlockstore(), - (c, b, p, a) -> CompletableFuture.completedFuture(true)) - .addProtocol(node1Http); - - Host node = builder.build(); - node.start().join(); - Thread shutdownHook = new Thread(() -> { - try { - node.stop(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - Runtime.getRuntime().addShutdownHook(shutdownHook); - - Multiaddr address1 = node.listenAddresses().get(0); - System.out.println("My address=" + address1); - - System.out.println("Enter address of other node:"); - Scanner in = new Scanner(System.in); - String address2 = in.nextLine(); - if (address2.length() == 0) { - System.err.println("Invalid address"); - } else { - Multiaddr targetAddress = new Multiaddr(address2); - while (true) { - System.out.println("Enter msg:"); - byte[] msg = in.nextLine().getBytes(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg)); - httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg.length); - HttpProtocol.HttpController proxier = node1Http.dial(node, targetAddress).getController().join(); - proxier.send(httpRequest.retain()).join().release(); - } - } - } - public static void main(String[] args) { - new Chat(); - } -} From a9445b66c557ce59b746444e37e44b0c8519f6bb Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 15 Sep 2023 09:00:18 +0100 Subject: [PATCH 005/218] WIP - put back chat using embeddedIPFS --- examples/chat/Chat.java | 124 ++++++++++++++++++++++++++++++++++++++ examples/chat/Chat2.java | 125 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 examples/chat/Chat.java create mode 100644 examples/chat/Chat2.java diff --git a/examples/chat/Chat.java b/examples/chat/Chat.java new file mode 100644 index 00000000..0b4cf723 --- /dev/null +++ b/examples/chat/Chat.java @@ -0,0 +1,124 @@ +package chat; + +import io.ipfs.multiaddr.MultiAddress; +import io.ipfs.multihash.Multihash; +import io.libp2p.core.AddressBook; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.multiformats.Multiaddr; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.*; +import org.peergos.*; +import org.peergos.blockstore.Blockstore; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.config.Config; +import org.peergos.config.IdentitySection; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.RamRecordStore; +import org.peergos.protocol.dht.RecordStore; +import org.peergos.protocol.http.HttpProtocol; +import org.peergos.util.Version; + +import java.io.Console; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; + +public class Chat { + + private EmbeddedIpfs embeddedIpfs; + + public static final Version CURRENT_VERSION = Version.parse("0.0.1"); + + private static final Logger LOG = Logger.getGlobal(); + + private static HttpProtocol.HttpRequestProcessor proxyHandler() { + return (s, req, h) -> { + ByteBuf content = ((FullHttpRequest) req).content(); + CharSequence contents = content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset()); + String output = contents.toString(); + System.out.println("received msg:" + output); + }; + } + + public Chat() { + RecordStore recordStore = new RamRecordStore(); + Blockstore blockStore = new RamBlockstore(); + + LOG.info("Starting Chat version: " + CURRENT_VERSION); + int portNumber = 10000 + new Random().nextInt(50000); + List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/" + portNumber)); + List bootstrapNodes = new ArrayList<>(Config.defaultBootstrapNodes); + + HostBuilder builder = new HostBuilder().generateIdentity(); + PrivKey privKey = builder.getPrivateKey(); + PeerId peerId = builder.getPeerId(); + System.out.println("My PeerId:" + peerId.toBase58()); + IdentitySection identitySection = new IdentitySection(privKey.bytes(), peerId); + BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); + + embeddedIpfs = EmbeddedIpfs.build(recordStore, blockStore, + swarmAddresses, + bootstrapNodes, + identitySection, + authoriser, Optional.of(Chat.proxyHandler())); + embeddedIpfs.start(); + Thread shutdownHook = new Thread(() -> { + LOG.info("Stopping server..."); + try { + embeddedIpfs.stop().join(); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + Runtime.getRuntime().addShutdownHook(shutdownHook); + + System.out.println("Enter PeerId of other node:"); + Scanner in = new Scanner(System.in); + String peerIdStr = in.nextLine().trim(); + if (peerIdStr.length() == 0) { + throw new IllegalArgumentException("Invalid PeerId"); + } + Multihash targetNodeId = Multihash.fromBase58(peerIdStr); + runChat(embeddedIpfs.node, embeddedIpfs.dht, embeddedIpfs.p2pHttp.get(), targetNodeId); + } + private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBinding, Multihash targetNodeId) { + + AddressBook addressBook = node.getAddressBook(); + PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); + Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); + Multiaddr[] allAddresses = null; + if (targetAddressesOpt.isEmpty()) { + List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); + Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); + if (matching.isEmpty()) { + throw new IllegalStateException("Target not found: " + targetNodeId); + } + PeerAddresses peer = matching.get(); + allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); + } + Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? + Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) + : allAddresses; + byte[] msg = "world!".getBytes(); + HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, + HttpMethod.GET, "/", Unpooled.wrappedBuffer(msg)); + + HttpHeaders reqHeaders = httpRequest.headers(); + reqHeaders.set(HttpHeaderNames.CONTENT_LENGTH, msg.length); + FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); + int code = resp.status().code(); + resp.release(); + System.currentTimeMillis(); + } + + public static void main(String[] args) { + new Chat(); + } + +} diff --git a/examples/chat/Chat2.java b/examples/chat/Chat2.java new file mode 100644 index 00000000..bf734aaa --- /dev/null +++ b/examples/chat/Chat2.java @@ -0,0 +1,125 @@ +package chat; + +import io.ipfs.multiaddr.MultiAddress; +import io.ipfs.multihash.Multihash; +import io.libp2p.core.AddressBook; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.multiformats.Multiaddr; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.*; +import org.peergos.BlockRequestAuthoriser; +import org.peergos.EmbeddedIpfs; +import org.peergos.HostBuilder; +import org.peergos.PeerAddresses; +import org.peergos.blockstore.Blockstore; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.config.Config; +import org.peergos.config.IdentitySection; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.RamRecordStore; +import org.peergos.protocol.dht.RecordStore; +import org.peergos.protocol.http.HttpProtocol; +import org.peergos.util.Version; + +import java.nio.charset.Charset; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; + +public class Chat2 { + + private EmbeddedIpfs embeddedIpfs; + + public static final Version CURRENT_VERSION = Version.parse("0.0.1"); + + private static final Logger LOG = Logger.getGlobal(); + + private static HttpProtocol.HttpRequestProcessor proxyHandler() { + return (s, req, h) -> { + ByteBuf content = ((FullHttpRequest) req).content(); + CharSequence contents = content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset()); + String output = contents.toString(); + System.out.println("received msg:" + output); + }; + } + + public Chat2() { + RecordStore recordStore = new RamRecordStore(); + Blockstore blockStore = new RamBlockstore(); + + LOG.info("Starting Chat version: " + CURRENT_VERSION); + int portNumber = 10000 + new Random().nextInt(50000); + List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/" + portNumber)); + List bootstrapNodes = new ArrayList<>(Config.defaultBootstrapNodes); + + HostBuilder builder = new HostBuilder().generateIdentity(); + PrivKey privKey = builder.getPrivateKey(); + PeerId peerId = builder.getPeerId(); + System.out.println("My PeerId:" + peerId.toBase58()); + IdentitySection identitySection = new IdentitySection(privKey.bytes(), peerId); + BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); + + embeddedIpfs = EmbeddedIpfs.build(recordStore, blockStore, + swarmAddresses, + bootstrapNodes, + identitySection, + authoriser, Optional.of(Chat2.proxyHandler())); + embeddedIpfs.start(); + Thread shutdownHook = new Thread(() -> { + LOG.info("Stopping server..."); + try { + embeddedIpfs.stop().join(); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + Runtime.getRuntime().addShutdownHook(shutdownHook); + + System.out.println("Enter PeerId of other node:"); + Scanner in = new Scanner(System.in); + String peerIdStr = in.nextLine().trim(); + if (peerIdStr.length() == 0) { + throw new IllegalArgumentException("Invalid PeerId"); + } + Multihash targetNodeId = Multihash.fromBase58(peerIdStr); + runChat(embeddedIpfs.node, embeddedIpfs.dht, embeddedIpfs.p2pHttp.get(), targetNodeId); + } + private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBinding, Multihash targetNodeId) { + + AddressBook addressBook = node.getAddressBook(); + PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); + Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); + Multiaddr[] allAddresses = null; + if (targetAddressesOpt.isEmpty()) { + List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); + Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); + if (matching.isEmpty()) { + throw new IllegalStateException("Target not found: " + targetNodeId); + } + PeerAddresses peer = matching.get(); + allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); + } + Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? + Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) + : allAddresses; + byte[] msg = "world!".getBytes(); + HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, + HttpMethod.GET, "/", Unpooled.wrappedBuffer(msg)); + + HttpHeaders reqHeaders = httpRequest.headers(); + reqHeaders.set(HttpHeaderNames.CONTENT_LENGTH, msg.length); + FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); + int code = resp.status().code(); + resp.release(); + System.currentTimeMillis(); + } + + public static void main(String[] args) { + new Chat2(); + } + +} From a0ee74b388e517c97e5c8fcba132bd694a23df9f Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 15 Sep 2023 09:25:45 +0100 Subject: [PATCH 006/218] wip --- examples/chat/Chat.java | 12 +++++++++++- examples/chat/Chat2.java | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/chat/Chat.java b/examples/chat/Chat.java index 0b4cf723..c1d028b8 100644 --- a/examples/chat/Chat.java +++ b/examples/chat/Chat.java @@ -99,12 +99,20 @@ private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBindin throw new IllegalStateException("Target not found: " + targetNodeId); } PeerAddresses peer = matching.get(); - allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); + allAddresses = peer.addresses.stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); } Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) : allAddresses; byte[] msg = "world!".getBytes(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg)); + httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg.length); + HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, addressesToDial[0]).getController().join(); + FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); + int code = resp.status().code(); + resp.release(); + System.currentTimeMillis(); + /* HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/", Unpooled.wrappedBuffer(msg)); @@ -115,6 +123,8 @@ private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBindin int code = resp.status().code(); resp.release(); System.currentTimeMillis(); + + */ } public static void main(String[] args) { diff --git a/examples/chat/Chat2.java b/examples/chat/Chat2.java index bf734aaa..6659b04d 100644 --- a/examples/chat/Chat2.java +++ b/examples/chat/Chat2.java @@ -100,12 +100,20 @@ private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBindin throw new IllegalStateException("Target not found: " + targetNodeId); } PeerAddresses peer = matching.get(); - allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); + allAddresses = peer.addresses.stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); } Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) : allAddresses; byte[] msg = "world!".getBytes(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg)); + httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg.length); + HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, addressesToDial[0]).getController().join(); + FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); + int code = resp.status().code(); + resp.release(); + System.currentTimeMillis(); + /* HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/", Unpooled.wrappedBuffer(msg)); @@ -116,6 +124,8 @@ private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBindin int code = resp.status().code(); resp.release(); System.currentTimeMillis(); + + */ } public static void main(String[] args) { From b18fb12df20334702da9492e4dbde316447958d3 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 15 Sep 2023 10:13:07 +0100 Subject: [PATCH 007/218] fixes --- examples/chat/Chat.java | 59 ++++++++--------- examples/chat/Chat2.java | 135 --------------------------------------- 2 files changed, 27 insertions(+), 167 deletions(-) delete mode 100644 examples/chat/Chat2.java diff --git a/examples/chat/Chat.java b/examples/chat/Chat.java index c1d028b8..f77186cb 100644 --- a/examples/chat/Chat.java +++ b/examples/chat/Chat.java @@ -10,19 +10,20 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.*; -import org.peergos.*; +import io.netty.util.CharsetUtil; +import org.peergos.BlockRequestAuthoriser; +import org.peergos.EmbeddedIpfs; +import org.peergos.HostBuilder; +import org.peergos.PeerAddresses; import org.peergos.blockstore.Blockstore; import org.peergos.blockstore.RamBlockstore; import org.peergos.config.Config; import org.peergos.config.IdentitySection; -import org.peergos.protocol.dht.Kademlia; import org.peergos.protocol.dht.RamRecordStore; import org.peergos.protocol.dht.RecordStore; import org.peergos.protocol.http.HttpProtocol; import org.peergos.util.Version; -import java.io.Console; -import java.io.IOException; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -36,12 +37,17 @@ public class Chat { private static final Logger LOG = Logger.getGlobal(); + + private static HttpProtocol.HttpRequestProcessor proxyHandler() { return (s, req, h) -> { ByteBuf content = ((FullHttpRequest) req).content(); CharSequence contents = content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset()); String output = contents.toString(); System.out.println("received msg:" + output); + FullHttpResponse replyOk = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.buffer(0)); + replyOk.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); + h.accept(replyOk.retain()); }; } @@ -84,16 +90,13 @@ public Chat() { throw new IllegalArgumentException("Invalid PeerId"); } Multihash targetNodeId = Multihash.fromBase58(peerIdStr); - runChat(embeddedIpfs.node, embeddedIpfs.dht, embeddedIpfs.p2pHttp.get(), targetNodeId); - } - private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBinding, Multihash targetNodeId) { - AddressBook addressBook = node.getAddressBook(); - PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); - Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); + AddressBook addressBook = embeddedIpfs.node.getAddressBook(); + PeerId targetPeerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); + Optional targetAddressesOpt = addressBook.get(targetPeerId).join().stream().findFirst(); Multiaddr[] allAddresses = null; if (targetAddressesOpt.isEmpty()) { - List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); + List closestPeers = embeddedIpfs.dht.findClosestPeers(targetNodeId, 1, embeddedIpfs.node); Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); if (matching.isEmpty()) { throw new IllegalStateException("Target not found: " + targetNodeId); @@ -104,27 +107,19 @@ private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBindin Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) : allAddresses; - byte[] msg = "world!".getBytes(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg)); - httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg.length); - HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, addressesToDial[0]).getController().join(); - FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); - int code = resp.status().code(); - resp.release(); - System.currentTimeMillis(); - /* - HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "/", Unpooled.wrappedBuffer(msg)); - - HttpHeaders reqHeaders = httpRequest.headers(); - reqHeaders.set(HttpHeaderNames.CONTENT_LENGTH, msg.length); - FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); - int code = resp.status().code(); - resp.release(); - System.currentTimeMillis(); - - */ + + runChat(embeddedIpfs.node, embeddedIpfs.p2pHttp.get(), targetPeerId, addressesToDial); + } + private void runChat(Host node, HttpProtocol.Binding p2pHttpBinding, PeerId targetPeerId, Multiaddr[] addressesToDial) { + System.out.println("Type message:"); + Scanner in = new Scanner(System.in); + while (true) { + byte[] msg = in.nextLine().trim().getBytes(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg)); + httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg.length); + HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, targetPeerId, addressesToDial).getController().join(); + proxier.send(httpRequest.retain()).join().release(); + } } public static void main(String[] args) { diff --git a/examples/chat/Chat2.java b/examples/chat/Chat2.java deleted file mode 100644 index 6659b04d..00000000 --- a/examples/chat/Chat2.java +++ /dev/null @@ -1,135 +0,0 @@ -package chat; - -import io.ipfs.multiaddr.MultiAddress; -import io.ipfs.multihash.Multihash; -import io.libp2p.core.AddressBook; -import io.libp2p.core.Host; -import io.libp2p.core.PeerId; -import io.libp2p.core.crypto.PrivKey; -import io.libp2p.core.multiformats.Multiaddr; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.*; -import org.peergos.BlockRequestAuthoriser; -import org.peergos.EmbeddedIpfs; -import org.peergos.HostBuilder; -import org.peergos.PeerAddresses; -import org.peergos.blockstore.Blockstore; -import org.peergos.blockstore.RamBlockstore; -import org.peergos.config.Config; -import org.peergos.config.IdentitySection; -import org.peergos.protocol.dht.Kademlia; -import org.peergos.protocol.dht.RamRecordStore; -import org.peergos.protocol.dht.RecordStore; -import org.peergos.protocol.http.HttpProtocol; -import org.peergos.util.Version; - -import java.nio.charset.Charset; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.logging.Logger; - -public class Chat2 { - - private EmbeddedIpfs embeddedIpfs; - - public static final Version CURRENT_VERSION = Version.parse("0.0.1"); - - private static final Logger LOG = Logger.getGlobal(); - - private static HttpProtocol.HttpRequestProcessor proxyHandler() { - return (s, req, h) -> { - ByteBuf content = ((FullHttpRequest) req).content(); - CharSequence contents = content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset()); - String output = contents.toString(); - System.out.println("received msg:" + output); - }; - } - - public Chat2() { - RecordStore recordStore = new RamRecordStore(); - Blockstore blockStore = new RamBlockstore(); - - LOG.info("Starting Chat version: " + CURRENT_VERSION); - int portNumber = 10000 + new Random().nextInt(50000); - List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/" + portNumber)); - List bootstrapNodes = new ArrayList<>(Config.defaultBootstrapNodes); - - HostBuilder builder = new HostBuilder().generateIdentity(); - PrivKey privKey = builder.getPrivateKey(); - PeerId peerId = builder.getPeerId(); - System.out.println("My PeerId:" + peerId.toBase58()); - IdentitySection identitySection = new IdentitySection(privKey.bytes(), peerId); - BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); - - embeddedIpfs = EmbeddedIpfs.build(recordStore, blockStore, - swarmAddresses, - bootstrapNodes, - identitySection, - authoriser, Optional.of(Chat2.proxyHandler())); - embeddedIpfs.start(); - Thread shutdownHook = new Thread(() -> { - LOG.info("Stopping server..."); - try { - embeddedIpfs.stop().join(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - Runtime.getRuntime().addShutdownHook(shutdownHook); - - System.out.println("Enter PeerId of other node:"); - Scanner in = new Scanner(System.in); - String peerIdStr = in.nextLine().trim(); - if (peerIdStr.length() == 0) { - throw new IllegalArgumentException("Invalid PeerId"); - } - Multihash targetNodeId = Multihash.fromBase58(peerIdStr); - runChat(embeddedIpfs.node, embeddedIpfs.dht, embeddedIpfs.p2pHttp.get(), targetNodeId); - } - private void runChat(Host node, Kademlia dht, HttpProtocol.Binding p2pHttpBinding, Multihash targetNodeId) { - - AddressBook addressBook = node.getAddressBook(); - PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); - Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); - Multiaddr[] allAddresses = null; - if (targetAddressesOpt.isEmpty()) { - List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); - Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); - if (matching.isEmpty()) { - throw new IllegalStateException("Target not found: " + targetNodeId); - } - PeerAddresses peer = matching.get(); - allAddresses = peer.addresses.stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); - } - Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? - Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) - : allAddresses; - byte[] msg = "world!".getBytes(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg)); - httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg.length); - HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, addressesToDial[0]).getController().join(); - FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); - int code = resp.status().code(); - resp.release(); - System.currentTimeMillis(); - /* - HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "/", Unpooled.wrappedBuffer(msg)); - - HttpHeaders reqHeaders = httpRequest.headers(); - reqHeaders.set(HttpHeaderNames.CONTENT_LENGTH, msg.length); - FullHttpResponse resp = proxier.send(httpRequest.retain()).join(); - int code = resp.status().code(); - resp.release(); - System.currentTimeMillis(); - - */ - } - - public static void main(String[] args) { - new Chat2(); - } - -} From 7bcd8cc8e943bff34ebca178d39378323e48319b Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 15 Sep 2023 11:01:49 +0100 Subject: [PATCH 008/218] move chat to another repo --- examples/chat/Chat.java | 129 --------------------- src/main/java/org/peergos/net/Handler.java | 2 +- 2 files changed, 1 insertion(+), 130 deletions(-) delete mode 100644 examples/chat/Chat.java diff --git a/examples/chat/Chat.java b/examples/chat/Chat.java deleted file mode 100644 index f77186cb..00000000 --- a/examples/chat/Chat.java +++ /dev/null @@ -1,129 +0,0 @@ -package chat; - -import io.ipfs.multiaddr.MultiAddress; -import io.ipfs.multihash.Multihash; -import io.libp2p.core.AddressBook; -import io.libp2p.core.Host; -import io.libp2p.core.PeerId; -import io.libp2p.core.crypto.PrivKey; -import io.libp2p.core.multiformats.Multiaddr; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.*; -import io.netty.util.CharsetUtil; -import org.peergos.BlockRequestAuthoriser; -import org.peergos.EmbeddedIpfs; -import org.peergos.HostBuilder; -import org.peergos.PeerAddresses; -import org.peergos.blockstore.Blockstore; -import org.peergos.blockstore.RamBlockstore; -import org.peergos.config.Config; -import org.peergos.config.IdentitySection; -import org.peergos.protocol.dht.RamRecordStore; -import org.peergos.protocol.dht.RecordStore; -import org.peergos.protocol.http.HttpProtocol; -import org.peergos.util.Version; - -import java.nio.charset.Charset; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.logging.Logger; - -public class Chat { - - private EmbeddedIpfs embeddedIpfs; - - public static final Version CURRENT_VERSION = Version.parse("0.0.1"); - - private static final Logger LOG = Logger.getGlobal(); - - - - private static HttpProtocol.HttpRequestProcessor proxyHandler() { - return (s, req, h) -> { - ByteBuf content = ((FullHttpRequest) req).content(); - CharSequence contents = content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset()); - String output = contents.toString(); - System.out.println("received msg:" + output); - FullHttpResponse replyOk = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.buffer(0)); - replyOk.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); - h.accept(replyOk.retain()); - }; - } - - public Chat() { - RecordStore recordStore = new RamRecordStore(); - Blockstore blockStore = new RamBlockstore(); - - LOG.info("Starting Chat version: " + CURRENT_VERSION); - int portNumber = 10000 + new Random().nextInt(50000); - List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/" + portNumber)); - List bootstrapNodes = new ArrayList<>(Config.defaultBootstrapNodes); - - HostBuilder builder = new HostBuilder().generateIdentity(); - PrivKey privKey = builder.getPrivateKey(); - PeerId peerId = builder.getPeerId(); - System.out.println("My PeerId:" + peerId.toBase58()); - IdentitySection identitySection = new IdentitySection(privKey.bytes(), peerId); - BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); - - embeddedIpfs = EmbeddedIpfs.build(recordStore, blockStore, - swarmAddresses, - bootstrapNodes, - identitySection, - authoriser, Optional.of(Chat.proxyHandler())); - embeddedIpfs.start(); - Thread shutdownHook = new Thread(() -> { - LOG.info("Stopping server..."); - try { - embeddedIpfs.stop().join(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - Runtime.getRuntime().addShutdownHook(shutdownHook); - - System.out.println("Enter PeerId of other node:"); - Scanner in = new Scanner(System.in); - String peerIdStr = in.nextLine().trim(); - if (peerIdStr.length() == 0) { - throw new IllegalArgumentException("Invalid PeerId"); - } - Multihash targetNodeId = Multihash.fromBase58(peerIdStr); - - AddressBook addressBook = embeddedIpfs.node.getAddressBook(); - PeerId targetPeerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); - Optional targetAddressesOpt = addressBook.get(targetPeerId).join().stream().findFirst(); - Multiaddr[] allAddresses = null; - if (targetAddressesOpt.isEmpty()) { - List closestPeers = embeddedIpfs.dht.findClosestPeers(targetNodeId, 1, embeddedIpfs.node); - Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); - if (matching.isEmpty()) { - throw new IllegalStateException("Target not found: " + targetNodeId); - } - PeerAddresses peer = matching.get(); - allAddresses = peer.addresses.stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); - } - Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? - Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) - : allAddresses; - - runChat(embeddedIpfs.node, embeddedIpfs.p2pHttp.get(), targetPeerId, addressesToDial); - } - private void runChat(Host node, HttpProtocol.Binding p2pHttpBinding, PeerId targetPeerId, Multiaddr[] addressesToDial) { - System.out.println("Type message:"); - Scanner in = new Scanner(System.in); - while (true) { - byte[] msg = in.nextLine().trim().getBytes(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg)); - httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg.length); - HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, targetPeerId, addressesToDial).getController().join(); - proxier.send(httpRequest.retain()).join().release(); - } - } - - public static void main(String[] args) { - new Chat(); - } - -} diff --git a/src/main/java/org/peergos/net/Handler.java b/src/main/java/org/peergos/net/Handler.java index c372d731..4797ad6b 100755 --- a/src/main/java/org/peergos/net/Handler.java +++ b/src/main/java/org/peergos/net/Handler.java @@ -19,7 +19,7 @@ public Handler() { } - public abstract void handleCallToAPI(HttpExchange httpExchange); + abstract void handleCallToAPI(HttpExchange httpExchange); @Override public void handle(HttpExchange httpExchange) { From 574baa4a92ca5e4284c79474e13f5eaa0ffaec69 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 15 Sep 2023 15:12:06 +0100 Subject: [PATCH 009/218] extract out getAddresses from HttpProxyService --- src/main/java/org/peergos/EmbeddedIpfs.java | 23 ++++++++++++++++--- .../java/org/peergos/HttpProxyService.java | 21 +++-------------- .../org/peergos/net/ConnectionException.java | 12 ++++++++++ .../java/org/peergos/net/ProxyException.java | 12 ---------- 4 files changed, 35 insertions(+), 33 deletions(-) create mode 100644 src/main/java/org/peergos/net/ConnectionException.java delete mode 100644 src/main/java/org/peergos/net/ProxyException.java diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 51368025..a5ca4f5f 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -1,17 +1,15 @@ package org.peergos; -import identify.pb.*; import io.ipfs.multiaddr.*; -import io.ipfs.multihash.*; import io.ipfs.multihash.Multihash; import io.libp2p.core.*; import io.libp2p.core.multiformats.*; import io.libp2p.core.multistream.*; -import io.libp2p.etc.types.*; import io.libp2p.protocol.*; import org.peergos.blockstore.*; import org.peergos.blockstore.s3.S3Blockstore; import org.peergos.config.*; +import org.peergos.net.ConnectionException; import org.peergos.protocol.*; import org.peergos.protocol.autonat.*; import org.peergos.protocol.bitswap.*; @@ -157,4 +155,23 @@ public static EmbeddedIpfs build(RecordStore records, return new EmbeddedIpfs(node, blockstore, records, dht, bitswap, httpHandler, bootstrap); } + + public static Multiaddr[] getAddresses(Host node, Kademlia dht, Multihash targetNodeId) throws ConnectionException { + AddressBook addressBook = node.getAddressBook(); + PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); + Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); + Multiaddr[] allAddresses = null; + if (targetAddressesOpt.isEmpty()) { + List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); + Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); + if (matching.isEmpty()) { + throw new ConnectionException("Target not found: " + targetNodeId); + } + PeerAddresses peer = matching.get(); + allAddresses = peer.addresses.stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); + } + return targetAddressesOpt.isPresent() ? + Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) + : allAddresses; + } } diff --git a/src/main/java/org/peergos/HttpProxyService.java b/src/main/java/org/peergos/HttpProxyService.java index dd6b0b30..94a2e085 100755 --- a/src/main/java/org/peergos/HttpProxyService.java +++ b/src/main/java/org/peergos/HttpProxyService.java @@ -7,7 +7,7 @@ import io.libp2p.core.multiformats.Multiaddr; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.*; -import org.peergos.net.ProxyException; +import org.peergos.net.ConnectionException; import org.peergos.net.ProxyRequest; import org.peergos.net.ProxyResponse; import org.peergos.protocol.dht.Kademlia; @@ -34,24 +34,9 @@ public HttpProxyService(Host node, HttpProtocol.Binding p2pHttpBinding, Kademlia this.p2pHttpBinding = p2pHttpBinding; this.dht = dht; } - public ProxyResponse proxyRequest(Multihash targetNodeId, ProxyRequest request) throws IOException, ProxyException { - - AddressBook addressBook = node.getAddressBook(); + public ProxyResponse proxyRequest(Multihash targetNodeId, ProxyRequest request) throws IOException, ConnectionException { + Multiaddr[] addressesToDial = EmbeddedIpfs.getAddresses(node, dht, targetNodeId); PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); - Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); - Multiaddr[] allAddresses = null; - if (targetAddressesOpt.isEmpty()) { - List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); - Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); - if (matching.isEmpty()) { - throw new ProxyException("Target not found: " + targetNodeId); - } - PeerAddresses peer = matching.get(); - allAddresses = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); - } - Multiaddr[] addressesToDial = targetAddressesOpt.isPresent() ? - Arrays.asList(targetAddressesOpt.get()).toArray(Multiaddr[]::new) - : allAddresses; HttpProtocol.HttpController proxier = p2pHttpBinding.dial(node, peerId, addressesToDial).getController().join(); String urlParams = constructQueryParamString(request.queryParams); FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, diff --git a/src/main/java/org/peergos/net/ConnectionException.java b/src/main/java/org/peergos/net/ConnectionException.java new file mode 100644 index 00000000..ac133b47 --- /dev/null +++ b/src/main/java/org/peergos/net/ConnectionException.java @@ -0,0 +1,12 @@ +package org.peergos.net; + +public class ConnectionException extends Exception { + + public ConnectionException() { + super(); + } + public ConnectionException(String message) { + super(message); + } + +} diff --git a/src/main/java/org/peergos/net/ProxyException.java b/src/main/java/org/peergos/net/ProxyException.java deleted file mode 100644 index 8c69c96a..00000000 --- a/src/main/java/org/peergos/net/ProxyException.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.peergos.net; - -public class ProxyException extends Exception { - - public ProxyException() { - super(); - } - public ProxyException(String message) { - super(message); - } - -} From 463c153dc0a4a478c4d4fe5b0d8e209c5445ea89 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 15 Sep 2023 15:31:28 +0100 Subject: [PATCH 010/218] add shutdown hook implicitly so client does not need to --- src/main/java/org/peergos/EmbeddedIpfs.java | 19 ++++++++++++++++--- src/main/java/org/peergos/Nabu.java | 6 +----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index a5ca4f5f..78517b9e 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -81,6 +81,15 @@ public List getBlocks(List wants, Set peers, boolean public void start() { LOG.info("Starting IPFS..."); + Thread shutdownHook = new Thread(() -> { + LOG.info("Stopping Ipfs server..."); + try { + this.stop().join(); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + Runtime.getRuntime().addShutdownHook(shutdownHook); node.start().join(); IdentifyBuilder.addIdentifyProtocol(node); LOG.info("Node started and listening on " + node.listenAddresses()); @@ -93,9 +102,13 @@ public void start() { } public CompletableFuture stop() throws Exception { - records.close(); - blockProvider.stop(); - return node.stop(); + if (records != null) { + records.close(); + } + if (blockProvider != null) { + blockProvider.stop(); + } + return node != null ? node.stop() : CompletableFuture.completedFuture(null); } public static Blockstore buildBlockStore(Config config, Path ipfsPath) { diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index fc5b9aaf..d3537d60 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -2,14 +2,11 @@ import com.sun.net.httpserver.HttpServer; import io.ipfs.multiaddr.MultiAddress; -import io.netty.handler.codec.http.*; -import org.peergos.client.*; import org.peergos.config.*; import org.peergos.net.APIHandler; import org.peergos.net.HttpProxyHandler; import org.peergos.protocol.dht.DatabaseRecordStore; import org.peergos.protocol.http.*; -import org.peergos.util.HttpUtil; import org.peergos.util.JSONParser; import org.peergos.util.JsonHelper; import org.peergos.util.Logging; @@ -73,9 +70,8 @@ public Nabu(Args args) throws Exception { apiServer.start(); Thread shutdownHook = new Thread(() -> { - LOG.info("Stopping server..."); + LOG.info("Stopping API server..."); try { - ipfs.stop().join(); apiServer.stop(3); //wait max 3 seconds } catch (Exception ex) { ex.printStackTrace(); From e84a21257bce95687603c14ac84f8e84cccc3f1e Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 18 Sep 2023 12:13:52 +0100 Subject: [PATCH 011/218] attempt another build --- pom.xml | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 612903be..7e5fbe9b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.1.0-SNAPSHOT + v0.5.1 UTF-8 @@ -99,29 +99,13 @@ make-assembly - package + install single - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 - - - package - - shade - - - true - - - - From bc38c33d6d36685070f725e4c23a00a57973d02f Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 18 Sep 2023 12:22:35 +0100 Subject: [PATCH 012/218] revert failed pom experiment --- pom.xml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7e5fbe9b..f623d6d2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.5.1 + v0.5.0 UTF-8 @@ -99,13 +99,29 @@ make-assembly - install + package single + + org.apache.maven.plugins + maven-shade-plugin + 3.3.0 + + + package + + shade + + + true + + + + From 761955422ffccb524e28b16ddb370adc77e49939 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 20 Sep 2023 14:56:35 +0100 Subject: [PATCH 013/218] In the case when no peers are provided, make bitswap try connected peers before resorting to DHT findProviders --- .../java/org/peergos/BitswapBlockService.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/org/peergos/BitswapBlockService.java b/src/main/java/org/peergos/BitswapBlockService.java index b71f1416..26585b88 100644 --- a/src/main/java/org/peergos/BitswapBlockService.java +++ b/src/main/java/org/peergos/BitswapBlockService.java @@ -2,10 +2,12 @@ import io.ipfs.cid.*; import io.libp2p.core.*; +import io.libp2p.core.Stream; import org.peergos.protocol.bitswap.*; import org.peergos.protocol.dht.*; import java.util.*; +import java.util.concurrent.*; import java.util.stream.*; public class BitswapBlockService implements BlockService { @@ -23,10 +25,30 @@ public BitswapBlockService(Host us, Bitswap bitswap, Kademlia dht) { @Override public List get(List hashes, Set peers, boolean addToBlockstore) { if (peers.isEmpty()) { + // if peers are not provided try connected peers and then fallback to finding peers from DHT + Set connected = us.getStreams().stream() + .map(Stream::remotePeerId) + .collect(Collectors.toSet()); + Set fromConnected = bitswap.get(hashes, us, connected, addToBlockstore) + .stream() + .map(f -> f.orTimeout(10, TimeUnit.SECONDS).join()) + .collect(Collectors.toSet()); + if (fromConnected.size() == hashes.size()) + return new ArrayList<>(fromConnected); + + Set done = fromConnected.stream().map(b -> b.hash).collect(Collectors.toSet()); + List remaining = hashes.stream() + .filter(w -> !done.contains(w.cid)) + .collect(Collectors.toList()); List providers = dht.findProviders(hashes.get(0).cid, us, 5).join(); peers = providers.stream() .map(p -> PeerId.fromBase58(p.peerId.toBase58())) .collect(Collectors.toSet()); + return java.util.stream.Stream.concat(bitswap.get(remaining, us, peers, addToBlockstore) + .stream() + .map(f -> f.join()), + fromConnected.stream()) + .collect(Collectors.toList()); } return bitswap.get(hashes, us, peers, addToBlockstore) .stream() From 1b9331fc8dd0ba8e3197ca90194901cd494b711f Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 20 Sep 2023 16:50:08 +0100 Subject: [PATCH 014/218] Tolerate errors in the bitswap dial to connected peers --- src/main/java/org/peergos/BitswapBlockService.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/BitswapBlockService.java b/src/main/java/org/peergos/BitswapBlockService.java index 26585b88..bebd64d3 100644 --- a/src/main/java/org/peergos/BitswapBlockService.java +++ b/src/main/java/org/peergos/BitswapBlockService.java @@ -31,7 +31,13 @@ public List get(List hashes, Set peers, boolean addTo .collect(Collectors.toSet()); Set fromConnected = bitswap.get(hashes, us, connected, addToBlockstore) .stream() - .map(f -> f.orTimeout(10, TimeUnit.SECONDS).join()) + .flatMap(f -> { + try { + return java.util.stream.Stream.of(f.orTimeout(10, TimeUnit.SECONDS).join()); + } catch (Exception e) { + return java.util.stream.Stream.empty(); + } + }) .collect(Collectors.toSet()); if (fromConnected.size() == hashes.size()) return new ArrayList<>(fromConnected); From a8c5936b491d4c626a445e4f24667d623ea8e143 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 22 Sep 2023 16:50:41 +0100 Subject: [PATCH 015/218] Make sure outgoing connections from any protocol are candidates for bitswap broadcast Make bitswap tolerate errors connecting to some peers disable TLS by default until it is less noisy (pun intended) --- .../java/org/peergos/BitswapBlockService.java | 4 +- src/main/java/org/peergos/HostBuilder.java | 4 +- .../org/peergos/protocol/bitswap/Bitswap.java | 46 ++++++++++++++++--- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/peergos/BitswapBlockService.java b/src/main/java/org/peergos/BitswapBlockService.java index bebd64d3..f76743d9 100644 --- a/src/main/java/org/peergos/BitswapBlockService.java +++ b/src/main/java/org/peergos/BitswapBlockService.java @@ -26,9 +26,7 @@ public BitswapBlockService(Host us, Bitswap bitswap, Kademlia dht) { public List get(List hashes, Set peers, boolean addToBlockstore) { if (peers.isEmpty()) { // if peers are not provided try connected peers and then fallback to finding peers from DHT - Set connected = us.getStreams().stream() - .map(Stream::remotePeerId) - .collect(Collectors.toSet()); + Set connected = bitswap.getBroadcastAudience(); Set fromConnected = bitswap.get(hashes, us, connected, addToBlockstore) .stream() .flatMap(f -> { diff --git a/src/main/java/org/peergos/HostBuilder.java b/src/main/java/org/peergos/HostBuilder.java index fe7dbcce..a9c3f58b 100644 --- a/src/main/java/org/peergos/HostBuilder.java +++ b/src/main/java/org/peergos/HostBuilder.java @@ -148,7 +148,7 @@ public static Host build(PrivKey privKey, b.getIdentity().setFactory(() -> privKey); b.getTransports().add(TcpTransport::new); b.getSecureChannels().add(NoiseXXSecureChannel::new); - b.getSecureChannels().add(TlsSecureChannel::new); +// b.getSecureChannels().add(TlsSecureChannel::new); b.getMuxers().addAll(muxers); RamAddressBook addrs = new RamAddressBook(); @@ -160,6 +160,8 @@ public static Host build(PrivKey privKey, b.getProtocols().add(protocol); if (protocol instanceof AddressBookConsumer) ((AddressBookConsumer) protocol).setAddressBook(addrs); + if (protocol instanceof ConnectionHandler) + b.getConnectionHandlers().add((ConnectionHandler) protocol); } // Send an identify req on all new incoming connections diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index 791b1c62..10f44b03 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -5,6 +5,7 @@ import io.libp2p.core.*; import io.libp2p.core.multiformats.*; import io.libp2p.core.multistream.*; +import org.jetbrains.annotations.*; import org.peergos.*; import org.peergos.protocol.bitswap.pb.*; import org.peergos.util.*; @@ -15,11 +16,12 @@ import java.util.logging.*; import java.util.stream.*; -public class Bitswap extends StrictProtocolBinding implements AddressBookConsumer { +public class Bitswap extends StrictProtocolBinding implements AddressBookConsumer, ConnectionHandler { private static final Logger LOG = Logger.getLogger(Bitswap.class.getName()); public static int MAX_MESSAGE_SIZE = 2*1024*1024; private final BitswapEngine engine; + private final LRUCache connected = new LRUCache<>(100); private AddressBook addrs; public Bitswap(BitswapEngine engine) { @@ -32,6 +34,15 @@ public void setAddressBook(AddressBook addrs) { this.addrs = addrs; } + @Override + public void handleConnection(@NotNull Connection connection) { + // add all outgoing connections to an LRU of candidates + if (connection.isInitiator()) { + PeerId remoteId = connection.secureSession().getRemoteId(); + connected.put(remoteId, true); + } + } + public CompletableFuture get(Want hash, Host us, Set peers, @@ -62,12 +73,18 @@ public List> get(List wants, return results; } + public Set getBroadcastAudience() { + HashSet res = new HashSet<>(engine.getConnected()); + res.addAll(connected.keySet()); + return res; + } + public void sendWants(Host us, Set peers) { Set wants = engine.getWants(); LOG.fine("Broadcast wants: " + wants.size()); Map haves = engine.getHaves(); - // broadcast to all connected peers if none are supplied - Set audience = peers.isEmpty() ? engine.getConnected() : peers; + // broadcast to all connected bitswap peers if none are supplied + Set audience = peers.isEmpty() ? getBroadcastAudience() : peers; List wantsProto = wants.stream() .map(want -> MessageOuterClass.Message.Wantlist.Entry.newBuilder() .setWantType(audience.size() <= 2 || haves.containsKey(want) ? @@ -78,9 +95,13 @@ public void sendWants(Host us, Set peers) { .build()) .collect(Collectors.toList()); engine.buildAndSendMessages(wantsProto, Collections.emptyList(), Collections.emptyList(), - msg -> audience.forEach(peer -> dialPeer(us, peer, c -> { - c.send(msg); - }))); + msg -> audience.forEach(peer -> { + try { + dialPeer(us, peer, c -> { + c.send(msg); + }); + } catch (Exception e) {} + })); } private void dialPeer(Host us, PeerId peer, Consumer action) { @@ -90,4 +111,17 @@ private void dialPeer(Host us, PeerId peer, Consumer action) BitswapController controller = dial(us, peer, addr).getController().join(); action.accept(controller); } + + public class LRUCache extends LinkedHashMap { + private final int cacheSize; + + public LRUCache(int cacheSize) { + super(16, 0.75f, true); + this.cacheSize = cacheSize; + } + + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() >= cacheSize; + } + } } From 40c0db430389a3a56265882815d5390f4db0adbf Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Sep 2023 09:20:41 +0100 Subject: [PATCH 016/218] Remove busy loop in new block provider --- src/main/java/org/peergos/PeriodicBlockProvider.java | 6 +++--- .../java/org/peergos/blockstore/ProvidingBlockstore.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/peergos/PeriodicBlockProvider.java b/src/main/java/org/peergos/PeriodicBlockProvider.java index 84210b2f..8fc65457 100644 --- a/src/main/java/org/peergos/PeriodicBlockProvider.java +++ b/src/main/java/org/peergos/PeriodicBlockProvider.java @@ -19,13 +19,13 @@ public class PeriodicBlockProvider { private final Supplier> getBlocks; private final Host us; private final Kademlia dht; - private final Queue newBlocksToPublish; + private final BlockingDeque newBlocksToPublish; public PeriodicBlockProvider(long reprovideIntervalMillis, Supplier> getBlocks, Host us, Kademlia dht, - Queue newBlocksToPublish) { + BlockingDeque newBlocksToPublish) { this.reprovideIntervalMillis = reprovideIntervalMillis; this.getBlocks = getBlocks; this.us = us; @@ -58,7 +58,7 @@ public void run() { public void provideNewBlocks() { while (running.get()) { try { - Cid c = newBlocksToPublish.poll(); + Cid c = newBlocksToPublish.takeFirst(); if (c != null) { publish(Stream.of(c)); } diff --git a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java index e3d4251c..8f6a7602 100644 --- a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java +++ b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java @@ -9,7 +9,7 @@ public class ProvidingBlockstore implements Blockstore { private final Blockstore target; - public final Queue toPublish = new LinkedBlockingDeque<>(); + public final BlockingDeque toPublish = new LinkedBlockingDeque<>(); public ProvidingBlockstore(Blockstore target) { this.target = target; From a13f0c35aa43f88d1ed541c327ac0c93977a33d3 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Sep 2023 10:50:28 +0100 Subject: [PATCH 017/218] Log Infini filter ram size during construction --- src/main/java/org/peergos/blockstore/CidInfiniFilter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java index 43f552b1..8fb31e60 100644 --- a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java +++ b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java @@ -30,10 +30,11 @@ public static CidInfiniFilter build(Blockstore bs) { public static CidInfiniFilter build(Blockstore bs, double falsePositiveRate) { List refs = bs.refs().join(); - int nBlocks = refs.size(); + int nBlocks = refs.size()*5/4; // increase by 25% to avoid expansion during build int nextPowerOfTwo = Math.max(17, (int) (1 + Math.log(nBlocks) / Math.log(2))); double expansionAlpha = 0.8; int bitsPerEntry = (int)(4 - Math.log(falsePositiveRate / expansionAlpha) / Math.log(2) + 1); + System.out.println("Using infini filter of initial size " + ((double)(bitsPerEntry * (1 << nextPowerOfTwo) / 8) / 1024 / 1024) + " MiB"); ChainedInfiniFilter infini = new ChainedInfiniFilter(nextPowerOfTwo, bitsPerEntry); infini.set_expand_autonomously(true); refs.forEach(c -> infini.insert(c.toBytes(), true)); From 3d00db7c5339f110fd99e6ee693c45fc3e6f3511 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Thu, 28 Sep 2023 11:10:49 +0100 Subject: [PATCH 018/218] match log-to-console used in Peergos --- src/main/java/org/peergos/Nabu.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index d3537d60..2366c76f 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -34,7 +34,7 @@ private static HttpProtocol.HttpRequestProcessor proxyHandler(MultiAddress targe public Nabu(Args args) throws Exception { Path ipfsPath = getIPFSPath(args); - Logging.init(ipfsPath, args.getBoolean("logToConsole", false)); + Logging.init(ipfsPath, args.getBoolean("log-to-console", false)); Config config = readConfig(ipfsPath, args); if (config.metrics.enabled) { AggregatedMetrics.startExporter(config.metrics.address, config.metrics.port); From fa8cfd2ee8c1ed80f0336886cd4c0abb2816dec3 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Thu, 28 Sep 2023 14:45:42 +0100 Subject: [PATCH 019/218] fix logging --- src/main/java/org/peergos/AggregatedMetrics.java | 3 ++- src/main/java/org/peergos/EmbeddedIpfs.java | 3 ++- src/main/java/org/peergos/Nabu.java | 4 ++-- src/main/java/org/peergos/PeriodicBlockProvider.java | 3 ++- src/main/java/org/peergos/blockstore/FileBlockstore.java | 2 +- src/main/java/org/peergos/blockstore/s3/S3Blockstore.java | 2 +- src/main/java/org/peergos/protocol/bitswap/Bitswap.java | 2 +- src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java | 4 ++-- src/main/java/org/peergos/protocol/dht/Kademlia.java | 3 ++- 9 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/peergos/AggregatedMetrics.java b/src/main/java/org/peergos/AggregatedMetrics.java index 08cf7ee8..b61f58fe 100644 --- a/src/main/java/org/peergos/AggregatedMetrics.java +++ b/src/main/java/org/peergos/AggregatedMetrics.java @@ -2,6 +2,7 @@ import io.prometheus.client.Counter; import io.prometheus.client.exporter.HTTPServer; +import org.peergos.util.Logging; import java.io.IOException; import java.util.logging.Logger; @@ -10,7 +11,7 @@ * A wrapper around the prometheus metrics and HTTP exporter. */ public class AggregatedMetrics { - private static final Logger LOG = Logger.getLogger(Nabu.class.getName()); + private static final Logger LOG = Logging.LOG(); private static Counter build(String name, String help) { return Counter.build() diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 78517b9e..fb3c2a77 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -16,6 +16,7 @@ import org.peergos.protocol.circuit.*; import org.peergos.protocol.dht.*; import org.peergos.protocol.http.*; +import org.peergos.util.Logging; import java.nio.file.*; import java.util.*; @@ -24,7 +25,7 @@ import java.util.stream.*; public class EmbeddedIpfs { - private static final Logger LOG = Logger.getLogger(EmbeddedIpfs.class.getName()); + private static final Logger LOG = Logging.LOG(); public final Host node; public final ProvidingBlockstore blockstore; diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 2366c76f..b6882b5f 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -26,7 +26,7 @@ public class Nabu { - private static final Logger LOG = Logger.getLogger(Nabu.class.getName()); + private static final Logger LOG = Logging.LOG(); private static HttpProtocol.HttpRequestProcessor proxyHandler(MultiAddress target) { return (s, req, h) -> HttpProtocol.proxyRequest(req, new InetSocketAddress(target.getHost(), target.getPort()), h); @@ -34,7 +34,7 @@ private static HttpProtocol.HttpRequestProcessor proxyHandler(MultiAddress targe public Nabu(Args args) throws Exception { Path ipfsPath = getIPFSPath(args); - Logging.init(ipfsPath, args.getBoolean("log-to-console", false)); + Logging.init(ipfsPath, args.getBoolean("log-to-console", true)); Config config = readConfig(ipfsPath, args); if (config.metrics.enabled) { AggregatedMetrics.startExporter(config.metrics.address, config.metrics.port); diff --git a/src/main/java/org/peergos/PeriodicBlockProvider.java b/src/main/java/org/peergos/PeriodicBlockProvider.java index 8fc65457..5ee72a73 100644 --- a/src/main/java/org/peergos/PeriodicBlockProvider.java +++ b/src/main/java/org/peergos/PeriodicBlockProvider.java @@ -3,6 +3,7 @@ import io.ipfs.cid.*; import io.libp2p.core.*; import org.peergos.protocol.dht.*; +import org.peergos.util.Logging; import java.util.*; import java.util.concurrent.*; @@ -14,7 +15,7 @@ public class PeriodicBlockProvider { - private static final Logger LOG = Logger.getLogger(PeriodicBlockProvider.class.getName()); + private static final Logger LOG = Logging.LOG(); private final long reprovideIntervalMillis; private final Supplier> getBlocks; private final Host us; diff --git a/src/main/java/org/peergos/blockstore/FileBlockstore.java b/src/main/java/org/peergos/blockstore/FileBlockstore.java index 9edd9b76..41a87c65 100644 --- a/src/main/java/org/peergos/blockstore/FileBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FileBlockstore.java @@ -21,7 +21,7 @@ public class FileBlockstore implements Blockstore { - private static final Logger LOG = Logger.getLogger(FileBlockstore.class.getName()); + private static final Logger LOG = Logging.LOG(); private final Path blocksRoot; private final String BLOCKS = "blocks"; diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index df2c19a7..e8387dba 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -24,7 +24,7 @@ public class S3Blockstore implements Blockstore { - private static final Logger LOG = Logger.getLogger(S3Blockstore.class.getName()); + private static final Logger LOG = Logging.LOG(); private final String region; private final String bucket; diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index 10f44b03..36198f79 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -17,7 +17,7 @@ import java.util.stream.*; public class Bitswap extends StrictProtocolBinding implements AddressBookConsumer, ConnectionHandler { - private static final Logger LOG = Logger.getLogger(Bitswap.class.getName()); + private static final Logger LOG = Logging.LOG(); public static int MAX_MESSAGE_SIZE = 2*1024*1024; private final BitswapEngine engine; diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index f65309de..c30cc44c 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -19,7 +19,7 @@ import java.util.logging.*; public class BitswapEngine { - private static final Logger LOG = Logger.getLogger(BitswapEngine.class.getName()); + private static final Logger LOG = Logging.LOG(); private final Blockstore store; private final ConcurrentHashMap> localWants = new ConcurrentHashMap<>(); @@ -164,7 +164,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { Multihash.Type type = Multihash.Type.lookup((int)Cid.readVarint(bin)); // int hashSize = (int)Cid.readVarint(bin); if (type != Multihash.Type.sha2_256) { - Logger.getGlobal().info("Unsupported hash algorithm " + type.name()); + LOG.info("Unsupported hash algorithm " + type.name()); } else { byte[] hash = Hash.sha256(data); Cid c = new Cid(version, codec, type, hash); diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 750d8582..1cfe0ffa 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -12,6 +12,7 @@ import org.peergos.*; import org.peergos.protocol.dnsaddr.*; import org.peergos.protocol.ipns.*; +import org.peergos.util.Logging; import java.time.*; import java.util.*; @@ -23,7 +24,7 @@ public class Kademlia extends StrictProtocolBinding implements AddressBookConsumer { - private static final Logger LOG = Logger.getLogger(Kademlia.class.getName()); + private static final Logger LOG = Logging.LOG(); public static final int BOOTSTRAP_PERIOD_MILLIS = 300_000; private final KademliaEngine engine; private final boolean localDht; From 61c955e1d5bbf6cd83151a29c72f68f22e0ee142 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Sep 2023 14:52:50 +0100 Subject: [PATCH 020/218] Update jvm-libp2p to remove DNSADDR logspam --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f623d6d2..22867e16 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ com.github.peergos jvm-libp2p - 0.16.0 + 0.16.2 org.bouncycastle From b626c1e9a00974d36d0dfbc6434087183baa122a Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Sep 2023 16:36:21 +0100 Subject: [PATCH 021/218] Switch to log --- src/main/java/org/peergos/blockstore/CidInfiniFilter.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java index 8fb31e60..90f9a03c 100644 --- a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java +++ b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java @@ -2,11 +2,15 @@ import io.ipfs.cid.*; import org.peergos.blockstore.filters.*; +import org.peergos.util.Logging; import java.util.*; +import java.util.logging.*; public class CidInfiniFilter implements Filter { + private static final Logger LOG = Logging.LOG(); + private final ChainedInfiniFilter filter; private CidInfiniFilter(ChainedInfiniFilter filter) { @@ -34,7 +38,7 @@ public static CidInfiniFilter build(Blockstore bs, double falsePositiveRate) { int nextPowerOfTwo = Math.max(17, (int) (1 + Math.log(nBlocks) / Math.log(2))); double expansionAlpha = 0.8; int bitsPerEntry = (int)(4 - Math.log(falsePositiveRate / expansionAlpha) / Math.log(2) + 1); - System.out.println("Using infini filter of initial size " + ((double)(bitsPerEntry * (1 << nextPowerOfTwo) / 8) / 1024 / 1024) + " MiB"); + LOG.info("Using infini filter of initial size " + ((double)(bitsPerEntry * (1 << nextPowerOfTwo) / 8) / 1024 / 1024) + " MiB"); ChainedInfiniFilter infini = new ChainedInfiniFilter(nextPowerOfTwo, bitsPerEntry); infini.set_expand_autonomously(true); refs.forEach(c -> infini.insert(c.toBytes(), true)); From 0da2235b5edfb5b942018e42c0b5d81231204752 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Sep 2023 16:42:08 +0100 Subject: [PATCH 022/218] change default infini filter false positive rate --- src/main/java/org/peergos/blockstore/CidInfiniFilter.java | 2 +- src/test/java/org/peergos/blockstore/InfiniTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java index 90f9a03c..a838e035 100644 --- a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java +++ b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java @@ -29,7 +29,7 @@ public Cid add(Cid c) { } public static CidInfiniFilter build(Blockstore bs) { - return build(bs, 0.01); + return build(bs, 0.0001); } public static CidInfiniFilter build(Blockstore bs, double falsePositiveRate) { diff --git a/src/test/java/org/peergos/blockstore/InfiniTest.java b/src/test/java/org/peergos/blockstore/InfiniTest.java index c82528bf..28afe8e3 100644 --- a/src/test/java/org/peergos/blockstore/InfiniTest.java +++ b/src/test/java/org/peergos/blockstore/InfiniTest.java @@ -16,7 +16,7 @@ public void infini() { addRandomBlocks(nBlocks, bs); long t1 = System.currentTimeMillis(); - CidInfiniFilter infini = CidInfiniFilter.build(bs); + CidInfiniFilter infini = CidInfiniFilter.build(bs, 0.01); long t2 = System.currentTimeMillis(); System.out.println("Building filter took: " + (t2-t1)+ "ms"); List refs = bs.refs().join(); From df0f6a4872a32f9d48f252316e86a4e53c666e31 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Sep 2023 17:34:53 +0100 Subject: [PATCH 023/218] Better log statement --- src/main/java/org/peergos/net/HttpProxyHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/net/HttpProxyHandler.java b/src/main/java/org/peergos/net/HttpProxyHandler.java index 64003842..e883fc28 100755 --- a/src/main/java/org/peergos/net/HttpProxyHandler.java +++ b/src/main/java/org/peergos/net/HttpProxyHandler.java @@ -30,6 +30,7 @@ public HttpProxyHandler(HttpProxyService service) { public void handleCallToAPI(HttpExchange httpExchange) { long t1 = System.currentTimeMillis(); String path = httpExchange.getRequestURI().getPath(); + ProxyResponse response = null; try { if (path.startsWith(HttpProxyService.API_URL)) { // /p2p/$target_node_id/http/$target_path @@ -55,7 +56,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { ProxyRequest request = new ProxyRequest(targetPath, ProxyRequest.Method.valueOf(httpExchange.getRequestMethod()), reqHeaders, reqQueryParams, body); - ProxyResponse response = service.proxyRequest(targetNodeId, request); + response = service.proxyRequest(targetNodeId, request); Headers reponseHeaders = httpExchange.getResponseHeaders(); for (Map.Entry entry : response.headers.entrySet()) { reponseHeaders.set(entry.getKey(), entry.getValue()); @@ -72,7 +73,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { httpExchange.close(); long t2 = System.currentTimeMillis(); if (LOGGING) - LOG.info("API Handler handled " + path + " query in: " + (t2 - t1) + " mS"); + LOG.info("P2P HTTP proxy handled " + path + " query in: " + (t2 - t1) + " mS" + (response != null ? (", response code http " + response.statusCode) : "")); } } } From d63cea05d2cb4726c2895a215315829c196e62ca Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 29 Sep 2023 09:34:48 +0100 Subject: [PATCH 024/218] Add test for pinging between ed25519 and RSA nodes, with pre generated keys --- src/test/java/org/peergos/PingTest.java | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/test/java/org/peergos/PingTest.java b/src/test/java/org/peergos/PingTest.java index 8f02b639..28f75eb2 100644 --- a/src/test/java/org/peergos/PingTest.java +++ b/src/test/java/org/peergos/PingTest.java @@ -1,8 +1,13 @@ package org.peergos; import identify.pb.*; +import io.ipfs.multiaddr.*; +import io.ipfs.multihash.Multihash; import io.libp2p.core.*; +import io.libp2p.core.crypto.*; import io.libp2p.core.multiformats.*; +import io.libp2p.core.multistream.*; +import io.libp2p.crypto.keys.*; import io.libp2p.protocol.*; import org.junit.*; import org.peergos.blockstore.*; @@ -35,6 +40,46 @@ public void runPing() { } } + public static Host build(PrivKey keys, + int listenPort, + List protocols) { + return new HostBuilder() + .setIdentity(keys.bytes()) + .listen(List.of(new MultiAddress("/ip4/0.0.0.0/tcp/" + listenPort))) + .addProtocols(protocols) + .build(); + } + + @Test + public void runPingEd25519ToRSA() { + PrivKey node1Keys = Ed25519Kt.generateEd25519KeyPair().getFirst(); + int node1Port = TestPorts.getPort(); + Host node1 = build(node1Keys, node1Port, List.of(new Ping(), + new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); + PrivKey node2Keys = RsaKt.generateRsaKeyPair(2048).getFirst(); + int node2Port = TestPorts.getPort(); + Host node2 = build(node2Keys, node2Port, List.of(new Ping(), + new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); + node1.start().join(); + node2.start().join(); + + Assert.assertTrue(new Multihash(Multihash.Type.id, node1Keys.publicKey().bytes()).toString().equals(node1.getPeerId().toString())); + Assert.assertTrue(new Multihash(Multihash.Type.sha2_256, Hash.sha256(node2Keys.publicKey().bytes())).toString().equals(node2.getPeerId().toString())); + try { + Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/" + node2Port + "/p2p/" + node2.getPeerId()); + PingController pinger = new Ping().dial(node1, address2).getController().join(); + + System.out.println("Sending ping messages to " + address2); + for (int i = 0; i < 2; i++) { + long latency = pinger.ping().join(); + System.out.println("Ping " + i + ", latency " + latency + "ms"); + } + } finally { + node1.stop(); + node2.stop(); + } + } + @Test public void replyIdentifyOnNewDial() { Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); From 8591d11be21cb352d36d2402a15f2355eb23e202 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 3 Oct 2023 10:27:26 +0100 Subject: [PATCH 025/218] Make sure we start thread to publish our peerid + addresses to DHT! --- src/main/java/org/peergos/EmbeddedIpfs.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index fb3c2a77..ce83efd3 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -98,6 +98,7 @@ public void start() { int connections = dht.bootstrapRoutingTable(node, bootstrap, addr -> !addr.contains("/wss/")); LOG.info("Bootstrapping IPFS kademlia"); dht.bootstrap(node); + dht.startBootstrapThread(node); blockProvider.start(); } From 754e2afd3575a90ff7d59c0badba7686507f8c99 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 3 Oct 2023 10:30:56 +0100 Subject: [PATCH 026/218] Add identify init to ping test --- src/test/java/org/peergos/PingTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/org/peergos/PingTest.java b/src/test/java/org/peergos/PingTest.java index 28f75eb2..867a466b 100644 --- a/src/test/java/org/peergos/PingTest.java +++ b/src/test/java/org/peergos/PingTest.java @@ -62,6 +62,8 @@ public void runPingEd25519ToRSA() { new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); node1.start().join(); node2.start().join(); + IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node2); Assert.assertTrue(new Multihash(Multihash.Type.id, node1Keys.publicKey().bytes()).toString().equals(node1.getPeerId().toString())); Assert.assertTrue(new Multihash(Multihash.Type.sha2_256, Hash.sha256(node2Keys.publicKey().bytes())).toString().equals(node2.getPeerId().toString())); From a66ccc509ec49b251d323f76b5a378c7761d9197 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 3 Oct 2023 11:34:57 +0100 Subject: [PATCH 027/218] Add new test for EmbeddedIpfs --- .../java/org/peergos/EmbeddedIpfsTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/test/java/org/peergos/EmbeddedIpfsTest.java diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java new file mode 100644 index 00000000..8b0bdcce --- /dev/null +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -0,0 +1,47 @@ +package org.peergos; + +import io.ipfs.cid.*; +import io.ipfs.multiaddr.*; +import io.libp2p.core.*; +import io.libp2p.core.crypto.*; +import org.junit.*; +import org.peergos.blockstore.*; +import org.peergos.config.*; +import org.peergos.protocol.dht.*; + +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.*; + +public class EmbeddedIpfsTest { + + @Test + public void test() throws Exception { + EmbeddedIpfs node1 = build(Collections.emptyList()); + node1.start(); + EmbeddedIpfs node2 = build(node1.node.listenAddresses() + .stream() + .map(a -> new MultiAddress(a.toString())) + .collect(Collectors.toList())); + node2.start(); + + Cid block = node2.blockstore.put("G'day mate!".getBytes(), Cid.Codec.Raw).join(); + PeerId peerId2 = node2.node.getPeerId(); + List retrieved = node1.getBlocks(List.of(new Want(block)), Set.of(peerId2), false); + Assert.assertTrue(retrieved.size() == 1); + + node1.stop(); + node2.stop(); + } + + public static EmbeddedIpfs build(List bootstrap) { + int swarm = TestPorts.getPort(); + List swarmAddresses = List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + swarm)); + BlockRequestAuthoriser blockRequestAuthoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); + HostBuilder builder = new HostBuilder().generateIdentity(); + PrivKey privKey = builder.getPrivateKey(); + PeerId peerId = builder.getPeerId(); + IdentitySection id = new IdentitySection(privKey.bytes(), peerId); + return EmbeddedIpfs.build(new RamRecordStore(), new RamBlockstore(), swarmAddresses, bootstrap, id, blockRequestAuthoriser, Optional.empty()); + } +} From d43c363d78b9bb39c0fceb87c4ee683aeaabe319 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 3 Oct 2023 17:09:52 +0100 Subject: [PATCH 028/218] EmbeddedIpfsTest now fails with upstream yamux enabled --- src/main/java/org/peergos/Want.java | 5 +++++ src/test/java/org/peergos/EmbeddedIpfsTest.java | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/peergos/Want.java b/src/main/java/org/peergos/Want.java index db0ca64b..9de2b0fd 100644 --- a/src/main/java/org/peergos/Want.java +++ b/src/main/java/org/peergos/Want.java @@ -28,4 +28,9 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(cid, authHex); } + + @Override + public String toString() { + return cid.toString() + "(" + authHex.orElse("") + ")"; + } } diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 8b0bdcce..2ab5241a 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -16,7 +16,7 @@ public class EmbeddedIpfsTest { @Test - public void test() throws Exception { + public void largeBlock() throws Exception { EmbeddedIpfs node1 = build(Collections.emptyList()); node1.start(); EmbeddedIpfs node2 = build(node1.node.listenAddresses() @@ -25,9 +25,11 @@ public void test() throws Exception { .collect(Collectors.toList())); node2.start(); - Cid block = node2.blockstore.put("G'day mate!".getBytes(), Cid.Codec.Raw).join(); + Cid block = node2.blockstore.put(new byte[1024 * 1024], Cid.Codec.Raw).join(); PeerId peerId2 = node2.node.getPeerId(); - List retrieved = node1.getBlocks(List.of(new Want(block)), Set.of(peerId2), false); + List retrieved = ForkJoinPool.commonPool().submit( + () -> node1.getBlocks(List.of(new Want(block)), Set.of(peerId2), false)) + .get(5, TimeUnit.SECONDS); Assert.assertTrue(retrieved.size() == 1); node1.stop(); From 8a6aad5d71dbe153880557f4fb1f7caba3cfe32f Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 3 Oct 2023 17:13:23 +0100 Subject: [PATCH 029/218] Revert upstream yamux changes which break things --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 22867e16..f623d6d2 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ com.github.peergos jvm-libp2p - 0.16.2 + 0.16.0 org.bouncycastle From b6090ece369f82727cd0e143e10e282d86ade4fc Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 5 Oct 2023 14:37:45 +0100 Subject: [PATCH 030/218] Fix addresses we announce via Identify If we are listening on a wildcard address we need to expand it, and find all the matching interfaces and their ip addresses. Add a test to verify this to EmbeddedIpfsTest --- .../org/peergos/protocol/IdentifyBuilder.java | 56 +++++++++++++++++++ .../java/org/peergos/EmbeddedIpfsTest.java | 30 ++++++++-- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/protocol/IdentifyBuilder.java b/src/main/java/org/peergos/protocol/IdentifyBuilder.java index d19d3a59..f2a82dfa 100644 --- a/src/main/java/org/peergos/protocol/IdentifyBuilder.java +++ b/src/main/java/org/peergos/protocol/IdentifyBuilder.java @@ -3,11 +3,15 @@ import identify.pb.*; import io.libp2p.core.*; import io.libp2p.core.multiformats.*; +import io.libp2p.core.multiformats.Protocol; import io.libp2p.core.multistream.*; import io.libp2p.etc.types.*; import io.libp2p.protocol.*; +import java.net.*; +import java.util.*; import java.util.stream.*; +import java.util.stream.Stream; public class IdentifyBuilder { @@ -17,6 +21,7 @@ public static void addIdentifyProtocol(Host node) { .setAgentVersion("nabu/v0.1.0") .setPublicKey(ByteArrayExtKt.toProtobuf(node.getPrivKey().publicKey().bytes())) .addAllListenAddrs(node.listenAddresses().stream() + .flatMap(a -> expandWildcardAddresses(a).stream()) .map(Multiaddr::serialize) .map(ByteArrayExtKt::toProtobuf) .collect(Collectors.toList())); @@ -27,4 +32,55 @@ public static void addIdentifyProtocol(Host node) { Identify identify = new Identify(identifyBuilder.build()); node.addProtocolHandler(identify); } + + /* /ip6/::/tcp/4001 should expand to the following for example: + "/ip6/0:0:0:0:0:0:0:1/udp/4001/quic" + "/ip4/50.116.48.246/tcp/4001" + "/ip4/127.0.0.1/tcp/4001" + "/ip6/2600:3c03:0:0:f03c:92ff:fee7:bc1c/tcp/4001" + "/ip6/0:0:0:0:0:0:0:1/tcp/4001" + "/ip4/50.116.48.246/udp/4001/quic" + "/ip4/127.0.0.1/udp/4001/quic" + "/ip6/2600:3c03:0:0:f03c:92ff:fee7:bc1c/udp/4001/quic" + */ + public static List expandWildcardAddresses(Multiaddr addr) { + // Do not include /p2p or /ipfs components which are superfluous here + if (! isWildcard(addr)) + return List.of(new Multiaddr(addr.getComponents() + .stream() + .filter(c -> c.getProtocol() != Protocol.P2P + && c.getProtocol() != Protocol.IPFS) + .collect(Collectors.toList()))); + if (addr.has(Protocol.IP4)) + return listNetworkAddresses(false, addr); + if (addr.has(Protocol.IP6)) // include IP4 + return listNetworkAddresses(true, addr); + return Collections.emptyList(); + } + + public static List listNetworkAddresses(boolean includeIp6, Multiaddr addr) { + try { + return NetworkInterface.networkInterfaces() + .flatMap(net -> net.getInterfaceAddresses().stream() + .map(InterfaceAddress::getAddress) + .filter(ip -> includeIp6 || ip instanceof Inet4Address)) + .map(ip -> new Multiaddr(Stream.concat(Stream.of(new MultiaddrComponent(ip instanceof Inet4Address ? + Protocol.IP4 : + Protocol.IP6, ip.getAddress())), + addr.getComponents().stream() + .filter(c -> c.getProtocol() != Protocol.IP4 + && c.getProtocol() != Protocol.IP6 + && c.getProtocol() != Protocol.P2P + && c.getProtocol() != Protocol.IPFS)) + .collect(Collectors.toList()))) + .collect(Collectors.toList()); + } catch (SocketException e) { + throw new RuntimeException(e); + } + } + + public static boolean isWildcard(Multiaddr addr) { + String s = addr.toString(); + return s.contains("/::/") || s.contains("/0:0:0:0/"); + } } diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 2ab5241a..7f628ae0 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -1,9 +1,12 @@ package org.peergos; +import identify.pb.*; import io.ipfs.cid.*; import io.ipfs.multiaddr.*; import io.libp2p.core.*; import io.libp2p.core.crypto.*; +import io.libp2p.core.multiformats.*; +import io.libp2p.protocol.*; import org.junit.*; import org.peergos.blockstore.*; import org.peergos.config.*; @@ -17,12 +20,12 @@ public class EmbeddedIpfsTest { @Test public void largeBlock() throws Exception { - EmbeddedIpfs node1 = build(Collections.emptyList()); + EmbeddedIpfs node1 = build(Collections.emptyList(), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); node1.start(); EmbeddedIpfs node2 = build(node1.node.listenAddresses() .stream() .map(a -> new MultiAddress(a.toString())) - .collect(Collectors.toList())); + .collect(Collectors.toList()), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); node2.start(); Cid block = node2.blockstore.put(new byte[1024 * 1024], Cid.Codec.Raw).join(); @@ -36,9 +39,26 @@ public void largeBlock() throws Exception { node2.stop(); } - public static EmbeddedIpfs build(List bootstrap) { - int swarm = TestPorts.getPort(); - List swarmAddresses = List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + swarm)); + @Test + public void wildcardListenerAddressesGetExpanded() { + int node1Port = TestPorts.getPort(); + EmbeddedIpfs node1 = build(Collections.emptyList(), List.of(new MultiAddress("/ip6/::/tcp/" + node1Port))); + node1.start(); + + EmbeddedIpfs node2 = build(node1.node.listenAddresses() + .stream() + .map(a -> new MultiAddress(a.toString())) + .collect(Collectors.toList()), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); + node2.start(); + Multiaddr node1Addr = new Multiaddr("/ip4/127.0.0.1/tcp/" + node1Port + "/p2p/" + node1.node.getPeerId()); + IdentifyOuterClass.Identify id = new Identify().dial(node2.node, node1Addr).getController().join().id().join(); + List listening = id.getListenAddrsList().stream().map(b -> new MultiAddress(b.toByteArray())).collect(Collectors.toList()); + Assert.assertTrue(listening.stream().anyMatch(a -> a.toString().contains("/ip4/127.0.0.1"))); + Assert.assertTrue(listening.stream().noneMatch(a -> a.toString().contains("/p2p/"))); + Assert.assertTrue(listening.stream().noneMatch(a -> a.toString().contains("/ipfs/"))); + } + + public static EmbeddedIpfs build(List bootstrap, List swarmAddresses) { BlockRequestAuthoriser blockRequestAuthoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); HostBuilder builder = new HostBuilder().generateIdentity(); PrivKey privKey = builder.getPrivateKey(); From 28f829bcf26ad35d600517711d98825bddd542fd Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 6 Oct 2023 09:19:57 +0100 Subject: [PATCH 031/218] make datastore path absolute --- src/main/java/org/peergos/Nabu.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index b6882b5f..e84e9aab 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -43,7 +43,7 @@ public Nabu(Args args) throws Exception { BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); Path datastorePath = ipfsPath.resolve("datastore").resolve("h2.datastore"); - DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toString()); + DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toAbsolutePath().toString()); EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, buildBlockStore(config, ipfsPath), From a6bd87078f17888943c7d5222e9cb44704b26358 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 29 Sep 2023 17:39:14 +0100 Subject: [PATCH 032/218] WIP - add metadata store --- pom.xml | 15 + src/main/java/org/peergos/Args.java | 16 + src/main/java/org/peergos/EmbeddedIpfs.java | 13 +- src/main/java/org/peergos/Nabu.java | 11 +- .../org/peergos/blockstore/Blockstore.java | 11 + .../blockstore/metadatadb/BlockMetadata.java | 16 + .../metadatadb/BlockMetadataStore.java | 48 +++ .../blockstore/metadatadb/Builder.java | 59 ++++ .../metadatadb/JdbcBlockMetadataStore.java | 172 ++++++++++ .../metadatadb/MetadataCachingStorage.java | 99 ++++++ .../metadatadb/RamBlockMetadataStore.java | 52 +++ .../metadatadb/sql/PostgresCommands.java | 51 +++ .../metadatadb/sql/SqlSupplier.java | 40 +++ .../metadatadb/sql/SqliteCommands.java | 49 +++ .../peergos/blockstore/s3/S3Blockstore.java | 72 ++++- .../java/org/peergos/cbor/CborObject.java | 10 + src/main/java/org/peergos/util/Sqlite.java | 306 ++++++++++++++++++ .../java/org/peergos/S3BlockStoreTest.java | 5 +- 18 files changed, 1037 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/BlockMetadata.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/BlockMetadataStore.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/Builder.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/RamBlockMetadataStore.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/sql/PostgresCommands.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java create mode 100644 src/main/java/org/peergos/util/Sqlite.java diff --git a/pom.xml b/pom.xml index f623d6d2..c90c9f4d 100644 --- a/pom.xml +++ b/pom.xml @@ -220,5 +220,20 @@ simpleclient_httpserver 0.16.0 + + com.zaxxer + HikariCP + 5.0.1 + + + org.postgresql + postgresql + 42.6.0 + + + org.xerial + sqlite-jdbc + 3.43.0.0 + \ No newline at end of file diff --git a/src/main/java/org/peergos/Args.java b/src/main/java/org/peergos/Args.java index e9008411..224a173f 100755 --- a/src/main/java/org/peergos/Args.java +++ b/src/main/java/org/peergos/Args.java @@ -1,5 +1,7 @@ package org.peergos; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -12,6 +14,20 @@ public Args(Map params) { this.params = params; } + public Path getIPFSDir() { + return hasArg(Nabu.IPFS_PATH) ? Paths.get(getArg(Nabu.IPFS_PATH)) : Nabu.DEFAULT_IPFS_DIR_PATH; + } + + public Path fromIPFSDir(String fileName, String defaultName) { + Path peergosDir = getIPFSDir(); + String fName = defaultName == null ? getArg(fileName) : getArg(fileName, defaultName); + return peergosDir.resolve(fName); + } + + public Path fromIPFSDir(String fileName) { + return fromIPFSDir(fileName, null); + } + public List getAllArgs() { Map env = System.getenv(); return params.entrySet().stream() diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index ce83efd3..2a934e55 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -7,6 +7,8 @@ import io.libp2p.core.multistream.*; import io.libp2p.protocol.*; import org.peergos.blockstore.*; +import org.peergos.blockstore.metadatadb.BlockMetadataStore; +import org.peergos.blockstore.metadatadb.MetadataCachingStorage; import org.peergos.blockstore.s3.S3Blockstore; import org.peergos.config.*; import org.peergos.net.ConnectionException; @@ -113,12 +115,14 @@ public CompletableFuture stop() throws Exception { return node != null ? node.stop() : CompletableFuture.completedFuture(null); } - public static Blockstore buildBlockStore(Config config, Path ipfsPath) { + public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMetadataStore meta) { Blockstore blocks = null; if (config.datastore.blockMount.prefix.equals("flatfs.datastore")) { blocks = new FileBlockstore(ipfsPath); }else if (config.datastore.blockMount.prefix.equals("s3.datastore")) { - blocks = new S3Blockstore(config.datastore.blockMount.getParams()); + S3Blockstore s3blocks = new S3Blockstore(config.datastore.blockMount.getParams(), meta); + blocks = s3blocks; + s3blocks.updateMetadataStoreIfEmpty(); } else { throw new IllegalStateException("Unrecognized datastore prefix: " + config.datastore.blockMount.prefix); } @@ -132,8 +136,11 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath) { } else { throw new IllegalStateException("Unhandled filter type: " + config.datastore.filter.type); } - return config.datastore.allowedCodecs.codecs.isEmpty() ? + Blockstore filteredBlockStore = config.datastore.allowedCodecs.codecs.isEmpty() ? blockStore : new TypeLimitedBlockstore(blockStore, config.datastore.allowedCodecs.codecs); + + return config.datastore.blockMount.prefix.equals("s3.datastore") ? filteredBlockStore + : new MetadataCachingStorage(filteredBlockStore, meta); } public static EmbeddedIpfs build(RecordStore records, diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index e84e9aab..4ee03e5d 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -2,6 +2,8 @@ import com.sun.net.httpserver.HttpServer; import io.ipfs.multiaddr.MultiAddress; +import org.peergos.blockstore.metadatadb.BlockMetadataStore; +import org.peergos.blockstore.metadatadb.Builder; import org.peergos.config.*; import org.peergos.net.APIHandler; import org.peergos.net.HttpProxyHandler; @@ -16,6 +18,7 @@ import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.*; @@ -26,6 +29,10 @@ public class Nabu { + public static final String IPFS_PATH = "IPFS_PATH"; + public static final Path DEFAULT_IPFS_DIR_PATH = + Paths.get(System.getProperty("user.home"), ".ipfs"); + private static final Logger LOG = Logging.LOG(); private static HttpProtocol.HttpRequestProcessor proxyHandler(MultiAddress target) { @@ -44,9 +51,9 @@ public Nabu(Args args) throws Exception { Path datastorePath = ipfsPath.resolve("datastore").resolve("h2.datastore"); DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toAbsolutePath().toString()); - + BlockMetadataStore meta = Builder.buildBlockMetadata(args); EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, - buildBlockStore(config, ipfsPath), + buildBlockStore(config, ipfsPath, meta), config.addresses.getSwarmAddresses(), config.bootstrap.getBootstrapAddresses(), config.identity, diff --git a/src/main/java/org/peergos/blockstore/Blockstore.java b/src/main/java/org/peergos/blockstore/Blockstore.java index 79b9c833..42450278 100644 --- a/src/main/java/org/peergos/blockstore/Blockstore.java +++ b/src/main/java/org/peergos/blockstore/Blockstore.java @@ -3,6 +3,8 @@ import io.ipfs.cid.*; import io.ipfs.multibase.binary.Base32; import io.ipfs.multihash.Multihash; +import org.peergos.blockstore.metadatadb.BlockMetadata; +import org.peergos.blockstore.metadatadb.BlockMetadataStore; import java.util.*; import java.util.concurrent.*; @@ -33,4 +35,13 @@ default Cid keyToHash(String key) { CompletableFuture> refs(); CompletableFuture bloomAdd(Cid cid); + + default Optional getBlockMetadataStore() { + return Optional.empty(); + } + + default CompletableFuture getBlockMetadata(Cid h) { + return get(h) + .thenApply(rawOpt -> BlockMetadataStore.extractMetadata(h, rawOpt.get())); + } } \ No newline at end of file diff --git a/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadata.java b/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadata.java new file mode 100644 index 00000000..84ef9333 --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadata.java @@ -0,0 +1,16 @@ +package org.peergos.blockstore.metadatadb; + +import io.ipfs.cid.Cid; + +import java.util.List; + +public class BlockMetadata { + + public final int size; + public final List links; + + public BlockMetadata(int size, List links) { + this.size = size; + this.links = links; + } +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadataStore.java new file mode 100644 index 00000000..e1bbde68 --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadataStore.java @@ -0,0 +1,48 @@ +package org.peergos.blockstore.metadatadb; + +import io.ipfs.cid.Cid; +import org.peergos.cbor.CborObject; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public interface BlockMetadataStore { + + Optional get(Cid block); + + void put(Cid block, BlockMetadata meta); + + void remove(Cid block); + + long size(); + + Stream list(); + + Stream listCbor(); + + default BlockMetadata put(Cid block, byte[] data) { + BlockMetadata meta = extractMetadata(block, data); + put(block, meta); + return meta; + } + + static BlockMetadata extractMetadata(Cid block, byte[] data) { + if (block.codec == Cid.Codec.Raw) { + BlockMetadata meta = new BlockMetadata(data.length, Collections.emptyList()); + return meta; + } else { + CborObject cbor = CborObject.fromByteArray(data); + List links = cbor + .links().stream() + .map(h -> (Cid) h) + .collect(Collectors.toList()); + BlockMetadata meta = new BlockMetadata(data.length, links); + return meta; + } + } + + void compact(); +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/Builder.java b/src/main/java/org/peergos/blockstore/metadatadb/Builder.java new file mode 100644 index 00000000..5c73a489 --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/Builder.java @@ -0,0 +1,59 @@ +package org.peergos.blockstore.metadatadb; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.peergos.Args; +import org.peergos.blockstore.metadatadb.sql.PostgresCommands; +import org.peergos.blockstore.metadatadb.sql.SqliteCommands; +import org.peergos.util.Sqlite; + +import java.io.File; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; +import java.util.function.Supplier; + +public class Builder { + + public static Supplier getPostgresConnector(Args a, String prefix) { + String postgresHost = a.getArg(prefix + "postgres.host"); + int postgresPort = a.getInt(prefix + "postgres.port", 5432); + String databaseName = a.getArg(prefix + "postgres.database", "peergos"); + String postgresUsername = a.getArg(prefix + "postgres.username"); + String postgresPassword = a.getArg(prefix + "postgres.password"); + + Properties props = new Properties(); + props.setProperty("dataSourceClassName", "org.postgresql.ds.PGSimpleDataSource"); + props.setProperty("dataSource.serverName", postgresHost); + props.setProperty("dataSource.portNumber", "" + postgresPort); + props.setProperty("dataSource.user", postgresUsername); + props.setProperty("dataSource.password", postgresPassword); + props.setProperty("dataSource.databaseName", databaseName); + HikariConfig config = new HikariConfig(props); + HikariDataSource ds = new HikariDataSource(config); + + return () -> { + try { + return ds.getConnection(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + }; + } + + public static BlockMetadataStore buildBlockMetadata(Args a) { + try { + boolean usePostgres = a.getArg("block-metadata-db-type", "sqlite").equals("postgres"); + if (usePostgres) { + return new JdbcBlockMetadataStore(getPostgresConnector(a, "metadb."), new PostgresCommands()); + } else { + File metaFile = a.fromIPFSDir("block-metadata-sql-file", "blockmetadata.sql").toFile(); + Connection instance = new Sqlite.UncloseableConnection(Sqlite.build(metaFile.getPath())); + return new JdbcBlockMetadataStore(() -> instance, new SqliteCommands()); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java new file mode 100644 index 00000000..6414dac7 --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java @@ -0,0 +1,172 @@ +package org.peergos.blockstore.metadatadb; + + +import io.ipfs.cid.Cid; +import org.peergos.blockstore.metadatadb.sql.SqlSupplier; +import org.peergos.cbor.CborObject; +import org.peergos.util.Logging; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class JdbcBlockMetadataStore implements BlockMetadataStore { + + private static final Logger LOG = Logging.LOG(); + private static final String GET_INFO = "SELECT * FROM blockmetadata WHERE cid = ?;"; + private static final String REMOVE = "DELETE FROM blockmetadata where cid = ?;"; + private static final String LIST = "SELECT cid FROM blockmetadata;"; + private static final String SIZE = "SELECT COUNT(*) FROM blockmetadata;"; + private Supplier conn; + private final SqlSupplier commands; + + public JdbcBlockMetadataStore(Supplier conn, SqlSupplier commands) { + this.conn = conn; + this.commands = commands; + init(commands); + } + + private Connection getConnection() { + return getConnection(true, true); + } + + private Connection getConnection(boolean autocommit, boolean serializable) { + Connection connection = conn.get(); + try { + if (autocommit) + connection.setAutoCommit(true); + if (serializable) + connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); + return connection; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private synchronized void init(SqlSupplier commands) { + try (Connection conn = getConnection()) { + commands.createTable(commands.createBlockMetadataStoreTableCommand(), conn); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void compact() { + String vacuum = commands.vacuumCommand(); + if (vacuum.isEmpty()) + return; + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(vacuum)) { + stmt.executeUpdate(); + } catch (SQLException sqe) { + LOG.log(Level.WARNING, sqe.getMessage(), sqe); + throw new RuntimeException(sqe); + } + } + + public void remove(Cid block) { + try (Connection conn = getConnection(); + PreparedStatement insert = conn.prepareStatement(REMOVE)) { + + insert.setBytes(1, block.toBytes()); + insert.executeUpdate(); + } catch (SQLException sqe) { + LOG.log(Level.WARNING, sqe.getMessage(), sqe); + throw new RuntimeException(sqe); + } + } + + @Override + public Optional get(Cid block) { + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(GET_INFO)) { + stmt.setBytes(1, block.toBytes()); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + List links = ((CborObject.CborList) CborObject.fromByteArray(rs.getBytes("links"))) + .map(cbor -> Cid.cast(((CborObject.CborByteArray)cbor).value)); + return Optional.of(new BlockMetadata(rs.getInt("size"), links)); + } + return Optional.empty(); + } catch (SQLException sqe) { + LOG.log(Level.WARNING, sqe.getMessage(), sqe); + throw new RuntimeException(sqe); + } + } + + @Override + public void put(Cid block, BlockMetadata meta) { + try (Connection conn = getConnection(); + PreparedStatement insert = conn.prepareStatement(commands.addMetadataCommand())) { + + insert.setBytes(1, block.toBytes()); + insert.setLong(3, meta.size); + insert.setBytes(4, new CborObject.CborList(meta.links.stream() + .map(Cid::toBytes) + .map(CborObject.CborByteArray::new) + .collect(Collectors.toList())) + .toByteArray()); + insert.executeUpdate(); + } catch (SQLException sqe) { + LOG.log(Level.WARNING, sqe.getMessage(), sqe); + throw new RuntimeException(sqe); + } + } + + @Override + public long size() { + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(SIZE)) { + ResultSet rs = stmt.executeQuery(); + rs.next(); + return rs.getInt(1); + } catch (SQLException sqe) { + LOG.log(Level.WARNING, sqe.getMessage(), sqe); + throw new RuntimeException(sqe); + } + } + + @Override + public Stream list() { + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(LIST)) { + ResultSet rs = stmt.executeQuery(); + List res = new ArrayList<>(); + while (rs.next()) { + res.add(Cid.cast(rs.getBytes("cid"))); + } + return res.stream(); + } catch (SQLException sqe) { + LOG.log(Level.WARNING, sqe.getMessage(), sqe); + throw new RuntimeException(sqe); + } + } + + @Override + public Stream listCbor() { + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(LIST)) { + ResultSet rs = stmt.executeQuery(); + List res = new ArrayList<>(); + while (rs.next()) { + Cid cid = Cid.cast(rs.getBytes("cid")); + if (cid.codec != Cid.Codec.Raw) { + res.add(cid); + } + } + return res.stream(); + } catch (SQLException sqe) { + LOG.log(Level.WARNING, sqe.getMessage(), sqe); + throw new RuntimeException(sqe); + } + } +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java b/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java new file mode 100644 index 00000000..ca6afdf8 --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java @@ -0,0 +1,99 @@ +package org.peergos.blockstore.metadatadb; + +import io.ipfs.cid.Cid; +import io.ipfs.multihash.Multihash; +import org.peergos.Hash; +import org.peergos.blockstore.Blockstore; +import org.peergos.util.Futures; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +public class MetadataCachingStorage implements Blockstore { + + private final Blockstore target; + private final BlockMetadataStore metadata; + + public MetadataCachingStorage(Blockstore target, BlockMetadataStore metadata) { + this.target = target; + this.metadata = metadata; + } + + @Override + public Optional getBlockMetadataStore() { + return Optional.of(metadata); + } + + @Override + public CompletableFuture put(byte[] block, Cid.Codec codec) { + return target.put(block, codec) + .thenApply(cid -> { + metadata.put(cid, block); + return cid; + }); + } + + @Override + public CompletableFuture getBlockMetadata(Cid block) { + Optional meta = metadata.get(block); + if (meta.isPresent()) + return Futures.of(meta.get()); + return target.getBlockMetadata(block) + .thenApply(blockmeta -> { + metadata.put(block, blockmeta); + return blockmeta; + }); + } + + private void cacheBlockMetadata(byte[] block, boolean isRaw) { + Cid cid = hashToCid(block, isRaw); + metadata.put(cid, block); + } + + private Cid hashToCid(byte[] input, boolean isRaw) { + return buildCid(Hash.sha256(input), isRaw); + } + + private Cid buildCid(byte[] sha256, boolean isRaw) { + return Cid.buildCidV1(isRaw ? Cid.Codec.Raw : Cid.Codec.DagCbor, Multihash.Type.sha2_256, sha256); + } + + @Override + public CompletableFuture> get(Cid hash) { + return target.get(hash).thenApply(bopt -> { + bopt.ifPresent(b -> cacheBlockMetadata(b, hash.codec == Cid.Codec.Raw)); + return bopt; + }); + } + + @Override + public CompletableFuture has(Cid c) { + Optional meta = metadata.get(c); + if (meta.isPresent()) + return Futures.of(true); + return get(c).thenApply(opt -> opt.isPresent()); + } + + @Override + public CompletableFuture hasAny(Multihash h) { + return Futures.of(Stream.of(Cid.Codec.DagCbor, Cid.Codec.Raw, Cid.Codec.DagProtobuf) + .anyMatch(c -> has(new Cid(1, c, h.getType(), h.getHash())).join())); + } + + @Override + public CompletableFuture bloomAdd(Cid cid) { + return target.bloomAdd(cid); + } + + @Override + public CompletableFuture> refs() { + return target.refs(); + } + + @Override + public CompletableFuture rm(Cid c) { + return target.rm(c); + } +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/RamBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/RamBlockMetadataStore.java new file mode 100644 index 00000000..58836457 --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/RamBlockMetadataStore.java @@ -0,0 +1,52 @@ +package org.peergos.blockstore.metadatadb; + +import io.ipfs.cid.Cid; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +public class RamBlockMetadataStore implements BlockMetadataStore { + + private final Map store; + + public RamBlockMetadataStore() { + this.store = new HashMap<>(50_000); + } + + @Override + public Optional get(Cid block) { + return Optional.ofNullable(store.get(block)); + } + + @Override + public void put(Cid block, BlockMetadata meta) { + store.put(block, meta); + } + + @Override + public void remove(Cid block) { + store.remove(block); + } + + @Override + public Stream list() { + return store.keySet().stream(); + } + + @Override + public Stream listCbor() { + return store.keySet() + .stream() + .filter(c -> c.codec != Cid.Codec.Raw); + } + + @Override + public long size() { + return store.size(); + } + + @Override + public void compact() {} +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/PostgresCommands.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/PostgresCommands.java new file mode 100644 index 00000000..7db0e08e --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/PostgresCommands.java @@ -0,0 +1,51 @@ +package org.peergos.blockstore.metadatadb.sql; + +public class PostgresCommands implements SqlSupplier { + + @Override + public String vacuumCommand() { + return ""; + } + + @Override + public String listTablesCommand() { + return "SELECT tablename FROM pg_catalog.pg_tables " + + "WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema';"; + } + + @Override + public String tableExistsCommand() { + return "SELECT table_name FROM information_schema.tables WHERE table_schema LIKE 'public' AND table_type LIKE 'BASE TABLE' AND table_name = ?;"; + } + + @Override + public String addMetadataCommand() { + return "INSERT INTO blockmetadata (cid, size, links) VALUES(?, ?, ?) ON CONFLICT DO NOTHING;"; + } + + @Override + public String ensureColumnExistsCommand(String table, String column, String type) { + return "ALTER TABLE " + table + " ADD COLUMN IF NOT EXISTS " + column + " " + type + ";"; + } + + + @Override + public String insertOrIgnoreCommand(String prefix, String suffix) { + return prefix + suffix + " ON CONFLICT DO NOTHING;"; + } + + @Override + public String getByteArrayType() { + return "BYTEA"; + } + + @Override + public String getSerialIdType() { + return "SERIAL"; + } + + @Override + public String sqlInteger() { + return "BIGINT"; + } +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java new file mode 100644 index 00000000..65ce241e --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java @@ -0,0 +1,40 @@ +package org.peergos.blockstore.metadatadb.sql; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +public interface SqlSupplier { + + String listTablesCommand(); + + String tableExistsCommand(); + + String getByteArrayType(); + + String getSerialIdType(); + + String sqlInteger(); + + String ensureColumnExistsCommand(String table, String column, String type); + + String addMetadataCommand(); + + String vacuumCommand(); + + default String createBlockMetadataStoreTableCommand() { + return "CREATE TABLE IF NOT EXISTS blockmetadata (cid " + getByteArrayType() + " primary key not null, " + + "version varchar(160)," + + "size " + sqlInteger() + " not null, " + + "links " + getByteArrayType() + " not null, " + + "batids " + getByteArrayType() + " not null);"; + } + + String insertOrIgnoreCommand(String prefix, String suffix); + + default void createTable(String sqlTableCreate, Connection conn) throws SQLException { + Statement createStmt = conn.createStatement(); + createStmt.executeUpdate(sqlTableCreate); + createStmt.close(); + } +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java new file mode 100644 index 00000000..12711b51 --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java @@ -0,0 +1,49 @@ +package org.peergos.blockstore.metadatadb.sql; + +public class SqliteCommands implements SqlSupplier { + + @Override + public String vacuumCommand() { + return "VACUUM;"; + } + + @Override + public String listTablesCommand() { + return "SELECT NAME FROM sqlite_master WHERE type='table';"; + } + + @Override + public String tableExistsCommand() { + return "SELECT name FROM sqlite_master WHERE type='table' AND name=?;"; + } + + @Override + public String addMetadataCommand() { + return "INSERT OR IGNORE INTO blockmetadata (cid, size, links) VALUES(?, ?, ?);"; + } + + @Override + public String ensureColumnExistsCommand(String table, String column, String type) { + return "ALTER TABLE " + table + " ADD COLUMN " + column + " " + type + ";"; + } + + @Override + public String insertOrIgnoreCommand(String prefix, String suffix) { + return prefix + "OR IGNORE " + suffix + ";"; + } + + @Override + public String getByteArrayType() { + return "blob"; + } + + @Override + public String getSerialIdType() { + return "INTEGER"; + } + + @Override + public String sqlInteger() { + return "INTEGER"; + } +} diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index e8387dba..b0875500 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -5,6 +5,9 @@ import org.peergos.Hash; import org.peergos.blockstore.Blockstore; import org.peergos.blockstore.RateLimitException; +import org.peergos.blockstore.metadatadb.BlockMetadata; +import org.peergos.blockstore.metadatadb.BlockMetadataStore; +import org.peergos.cbor.CborObject; import org.peergos.util.Hasher; import org.peergos.util.*; @@ -16,6 +19,9 @@ import java.time.ZonedDateTime; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.logging.Level; @@ -38,7 +44,10 @@ public class S3Blockstore implements Blockstore { private final Hasher hasher; - public S3Blockstore(Map params) { + private final BlockMetadataStore blockMetadata; + + public S3Blockstore(Map params, BlockMetadataStore blockMetadata) { + this.blockMetadata = blockMetadata; region = getParam(params, "region"); bucket = getParam(params, "bucket"); regionEndpoint = getParam(params, "regionEndpoint", ""); @@ -145,6 +154,59 @@ private String getParam(Map params, String key, String defaultVa } } + @Override + public CompletableFuture getBlockMetadata(Cid h) { + if (h.getType() == Multihash.Type.id) + return Futures.of(new BlockMetadata(0, CborObject.getLinks(h, h.getHash()))); + Optional cached = blockMetadata.get(h); + if (cached.isPresent()) + return Futures.of(cached.get()); + Optional data = get(h).join(); + if (data.isEmpty()) + throw new IllegalStateException("Block not present locally: " + h); + byte[] bloc = data.get(); + if (h.codec == Cid.Codec.Raw) { + // we should avoid this by populating the metadata store, as it means two S3 calls, a ranged GET and a HEAD + int size = getSizeWithoutRetry(h).join().get(); + BlockMetadata meta = new BlockMetadata(size, Collections.emptyList()); + blockMetadata.put(h, meta); + return Futures.of(meta); + } + return Futures.of(blockMetadata.put(h, bloc)); + } + + public void updateMetadataStoreIfEmpty() { + if (blockMetadata.size() > 0) + return; + LOG.info("Updating block metadata store from S3. Listing blocks..."); + List all = refs().join(); + LOG.info("Updating block metadata store from S3. Updating db with " + all.size() + " blocks..."); + + int updateParallelism = 10; + ForkJoinPool pool = new ForkJoinPool(updateParallelism); + int batchSize = all.size() / updateParallelism; + AtomicLong progress = new AtomicLong(0); + int tenth = batchSize/10; + + List>> futures = IntStream.range(0, updateParallelism) + .mapToObj(b -> pool.submit(() -> IntStream.range(b * batchSize, (b + 1) * batchSize) + .mapToObj(i -> { + BlockMetadata res = getBlockMetadata(all.get(i)).join(); + if (i % (batchSize / 10) == 0) { + long updatedProgress = progress.addAndGet(tenth); + if (updatedProgress * 10 / all.size() > (updatedProgress - tenth) * 10 / all.size()) + LOG.info("Populating block metadata: " + updatedProgress * 100 / all.size() + "% done"); + } + return res; + }) + .reduce((x, y) -> y))) + .collect(Collectors.toList()); + futures.stream() + .map(ForkJoinTask::join) + .collect(Collectors.toList()); + LOG.info("Finished updating block metadata store from S3."); + } + private static V getWithBackoff(Supplier req) { long sleep = 100; for (int i=0; i < 20; i++) { @@ -172,6 +234,11 @@ public CompletableFuture hasAny(Multihash h) { } private CompletableFuture> getSizeWithoutRetry(Cid cid) { + Optional meta = blockMetadata.get(cid); + if (meta.isPresent()) + return Futures.of(Optional.of(meta.get().size)); + if (cid.getType() == Multihash.Type.id) // Identity hashes are not actually stored explicitly + return Futures.of(Optional.of(0)); try { PresignedUrl headUrl = S3Request.preSignHead(folder + hashToKey(cid), Optional.of(60), S3AdminRequests.asAwsDate(ZonedDateTime.now()), host, region, accessKeyId, secretKey, useHttps, hasher).join(); @@ -208,6 +275,7 @@ private CompletableFuture> getWithoutRetry(Cid cid) { S3AdminRequests.asAwsDate(ZonedDateTime.now()), host, region, accessKeyId, secretKey, useHttps, hasher).join(); try { byte[] block = HttpUtil.get(getUrl.base, getUrl.fields); + blockMetadata.put(cid, block); return Futures.of(Optional.of(block)); } catch (SocketTimeoutException | SSLException e) { // S3 can't handle the load so treat this as a rate limit and slow down @@ -243,6 +311,7 @@ public CompletableFuture putWithoutRetry(byte[] block, Cid.Codec codec) { PresignedUrl putUrl = S3Request.preSignPut(s3Key, block.length, contentHash, false, S3AdminRequests.asAwsDate(ZonedDateTime.now()), host, extraHeaders, region, accessKeyId, secretKey, useHttps, hasher).join(); HttpUtil.put(putUrl.base, putUrl.fields, block); + blockMetadata.put(cid, block); return CompletableFuture.completedFuture(cid); } catch (IOException e) { String msg = e.getMessage(); @@ -269,7 +338,6 @@ public CompletableFuture rm(Cid cid) { @Override public CompletableFuture bloomAdd(Cid cid) { - //not implemented return CompletableFuture.completedFuture(false); } diff --git a/src/main/java/org/peergos/cbor/CborObject.java b/src/main/java/org/peergos/cbor/CborObject.java index 80560083..145842b3 100644 --- a/src/main/java/org/peergos/cbor/CborObject.java +++ b/src/main/java/org/peergos/cbor/CborObject.java @@ -16,6 +16,16 @@ public interface CborObject extends Cborable { List links(); + static List getLinks(Cid h, byte[] data) { + return h.codec == Cid.Codec.Raw ? + Collections.emptyList() : + CborObject.fromByteArray(data) + .links() + .stream() + .map(m -> (Cid) m) + .collect(Collectors.toList()); + } + default byte[] toByteArray() { ByteArrayOutputStream bout = new ByteArrayOutputStream(); CborEncoder encoder = new CborEncoder(bout); diff --git a/src/main/java/org/peergos/util/Sqlite.java b/src/main/java/org/peergos/util/Sqlite.java new file mode 100644 index 00000000..4fe117ce --- /dev/null +++ b/src/main/java/org/peergos/util/Sqlite.java @@ -0,0 +1,306 @@ +package org.peergos.util; + +import org.peergos.Args; +import org.sqlite.*; + +import java.sql.*; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +public class Sqlite { + + public static Connection build(String dbPath) throws SQLException { + String url = "jdbc:sqlite:"+dbPath; + SQLiteDataSource dc = new SQLiteDataSource(); + dc.setUrl(url); + + Connection conn = dc.getConnection(); + conn.setAutoCommit(true); + return conn; + } + + public static String getDbPath(Args a, String type) { + String sqlFile = a.getArg(type); + return sqlFile.equals(":memory:") ? sqlFile : a.fromIPFSDir(type).toString(); + } + + public static class UncloseableConnection implements Connection { + + private final Connection target; + + public UncloseableConnection(Connection target) { + this.target = target; + } + + @Override + public Statement createStatement() throws SQLException { + return target.createStatement(); + } + + @Override + public PreparedStatement prepareStatement(String s) throws SQLException { + return target.prepareStatement(s); + } + + @Override + public CallableStatement prepareCall(String s) throws SQLException { + return target.prepareCall(s); + } + + @Override + public String nativeSQL(String s) throws SQLException { + return target.nativeSQL(s); + } + + @Override + public void setAutoCommit(boolean b) throws SQLException { + target.setAutoCommit(b); + } + + @Override + public boolean getAutoCommit() throws SQLException { + return target.getAutoCommit(); + } + + @Override + public void commit() throws SQLException { + target.commit(); + } + + @Override + public void rollback() throws SQLException { + target.rollback(); + } + + @Override + public void close() throws SQLException { + // Do nothing + } + + @Override + public boolean isClosed() throws SQLException { + return false; + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return target.getMetaData(); + } + + @Override + public void setReadOnly(boolean b) throws SQLException { + target.setReadOnly(b); + } + + @Override + public boolean isReadOnly() throws SQLException { + return target.isReadOnly(); + } + + @Override + public void setCatalog(String s) throws SQLException { + target.setCatalog(s); + } + + @Override + public String getCatalog() throws SQLException { + return target.getCatalog(); + } + + @Override + public void setTransactionIsolation(int i) throws SQLException { + target.setTransactionIsolation(i); + } + + @Override + public int getTransactionIsolation() throws SQLException { + return target.getTransactionIsolation(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return target.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + target.clearWarnings(); + } + + @Override + public Statement createStatement(int i, int i1) throws SQLException { + return target.createStatement(i, i1); + } + + @Override + public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException { + return target.prepareStatement(s, i, i1); + } + + @Override + public CallableStatement prepareCall(String s, int i, int i1) throws SQLException { + return target.prepareCall(s, i, i1); + } + + @Override + public Map> getTypeMap() throws SQLException { + return target.getTypeMap(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + target.setTypeMap(map); + } + + @Override + public void setHoldability(int i) throws SQLException { + target.setHoldability(i); + } + + @Override + public int getHoldability() throws SQLException { + return target.getHoldability(); + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return target.setSavepoint(); + } + + @Override + public Savepoint setSavepoint(String s) throws SQLException { + return target.setSavepoint(s); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + target.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + target.releaseSavepoint(savepoint); + } + + @Override + public Statement createStatement(int i, int i1, int i2) throws SQLException { + return target.createStatement(i, i1, i2); + } + + @Override + public PreparedStatement prepareStatement(String s, int i, int i1, int i2) throws SQLException { + return target.prepareStatement(s, i, i1, i2); + } + + @Override + public CallableStatement prepareCall(String s, int i, int i1, int i2) throws SQLException { + return target.prepareCall(s, i, i1, i2); + } + + @Override + public PreparedStatement prepareStatement(String s, int i) throws SQLException { + return target.prepareStatement(s, i); + } + + @Override + public PreparedStatement prepareStatement(String s, int[] ints) throws SQLException { + return target.prepareStatement(s, ints); + } + + @Override + public PreparedStatement prepareStatement(String s, String[] strings) throws SQLException { + return target.prepareStatement(s, strings); + } + + @Override + public Clob createClob() throws SQLException { + return target.createClob(); + } + + @Override + public Blob createBlob() throws SQLException { + return target.createBlob(); + } + + @Override + public NClob createNClob() throws SQLException { + return target.createNClob(); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return target.createSQLXML(); + } + + @Override + public boolean isValid(int i) throws SQLException { + return target.isValid(i); + } + + @Override + public void setClientInfo(String s, String s1) throws SQLClientInfoException { + target.setClientInfo(s, s1); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + target.setClientInfo(properties); + } + + @Override + public String getClientInfo(String s) throws SQLException { + return target.getClientInfo(s); + } + + @Override + public Properties getClientInfo() throws SQLException { + return target.getClientInfo(); + } + + @Override + public Array createArrayOf(String s, Object[] objects) throws SQLException { + return target.createArrayOf(s, objects); + } + + @Override + public Struct createStruct(String s, Object[] objects) throws SQLException { + return target.createStruct(s, objects); + } + + @Override + public void setSchema(String s) throws SQLException { + target.setSchema(s); + } + + @Override + public String getSchema() throws SQLException { + return target.getSchema(); + } + + @Override + public void abort(Executor executor) throws SQLException { + target.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int i) throws SQLException { + target.setNetworkTimeout(executor, i); + } + + @Override + public int getNetworkTimeout() throws SQLException { + return target.getNetworkTimeout(); + } + + @Override + public T unwrap(Class aClass) throws SQLException { + return target.unwrap(aClass); + } + + @Override + public boolean isWrapperFor(Class aClass) throws SQLException { + return target.isWrapperFor(aClass); + } + } +} diff --git a/src/test/java/org/peergos/S3BlockStoreTest.java b/src/test/java/org/peergos/S3BlockStoreTest.java index 0f3f21ec..8dadab11 100644 --- a/src/test/java/org/peergos/S3BlockStoreTest.java +++ b/src/test/java/org/peergos/S3BlockStoreTest.java @@ -3,6 +3,8 @@ import io.ipfs.cid.Cid; import io.ipfs.multihash.Multihash; import org.junit.*; +import org.peergos.blockstore.metadatadb.BlockMetadataStore; +import org.peergos.blockstore.metadatadb.RamBlockMetadataStore; import org.peergos.blockstore.s3.S3Blockstore; import java.util.*; @@ -30,7 +32,8 @@ public void testFileStore() { params.put("accessKey", "test"); params.put("secretKey", "testdslocal"); - S3Blockstore bs = new S3Blockstore(params); + BlockMetadataStore metadata = new RamBlockMetadataStore(); + S3Blockstore bs = new S3Blockstore(params, metadata); String msg = "hello world!"; byte[] block = msg.getBytes(); Cid.Codec codec = Cid.Codec.Raw; From 06d68c04e80c91a9c59c95c70391316aa42f14e6 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 2 Oct 2023 10:32:16 +0100 Subject: [PATCH 033/218] add unit test --- .../blockstore/metadatadb/Builder.java | 2 +- .../metadatadb/JdbcBlockMetadataStore.java | 10 ++++----- .../metadatadb/MetadataCachingStorage.java | 7 +++++- .../metadatadb/sql/SqlSupplier.java | 4 +--- .../peergos/blockstore/s3/S3Blockstore.java | 1 + .../java/org/peergos/S3BlockStoreTest.java | 22 +++++++++++++++++-- 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/metadatadb/Builder.java b/src/main/java/org/peergos/blockstore/metadatadb/Builder.java index 5c73a489..09d7253f 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/Builder.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/Builder.java @@ -18,7 +18,7 @@ public class Builder { public static Supplier getPostgresConnector(Args a, String prefix) { String postgresHost = a.getArg(prefix + "postgres.host"); int postgresPort = a.getInt(prefix + "postgres.port", 5432); - String databaseName = a.getArg(prefix + "postgres.database", "peergos"); + String databaseName = a.getArg(prefix + "postgres.database", "nabu"); String postgresUsername = a.getArg(prefix + "postgres.username"); String postgresPassword = a.getArg(prefix + "postgres.password"); diff --git a/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java index 6414dac7..0a7d8358 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java @@ -75,10 +75,10 @@ public void compact() { public void remove(Cid block) { try (Connection conn = getConnection(); - PreparedStatement insert = conn.prepareStatement(REMOVE)) { + PreparedStatement remove = conn.prepareStatement(REMOVE)) { - insert.setBytes(1, block.toBytes()); - insert.executeUpdate(); + remove.setBytes(1, block.toBytes()); + remove.executeUpdate(); } catch (SQLException sqe) { LOG.log(Level.WARNING, sqe.getMessage(), sqe); throw new RuntimeException(sqe); @@ -109,8 +109,8 @@ public void put(Cid block, BlockMetadata meta) { PreparedStatement insert = conn.prepareStatement(commands.addMetadataCommand())) { insert.setBytes(1, block.toBytes()); - insert.setLong(3, meta.size); - insert.setBytes(4, new CborObject.CborList(meta.links.stream() + insert.setLong(2, meta.size); + insert.setBytes(3, new CborObject.CborList(meta.links.stream() .map(Cid::toBytes) .map(CborObject.CborByteArray::new) .collect(Collectors.toList())) diff --git a/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java b/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java index ca6afdf8..198dd777 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java @@ -94,6 +94,11 @@ public CompletableFuture> refs() { @Override public CompletableFuture rm(Cid c) { - return target.rm(c); + return target.rm(c).thenApply(res -> { + if (res) { + metadata.remove(c); + } + return res; + }); } } diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java index 65ce241e..31402d2b 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java @@ -24,10 +24,8 @@ public interface SqlSupplier { default String createBlockMetadataStoreTableCommand() { return "CREATE TABLE IF NOT EXISTS blockmetadata (cid " + getByteArrayType() + " primary key not null, " + - "version varchar(160)," + "size " + sqlInteger() + " not null, " + - "links " + getByteArrayType() + " not null, " + - "batids " + getByteArrayType() + " not null);"; + "links " + getByteArrayType() + " not null);"; } String insertOrIgnoreCommand(String prefix, String suffix); diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index b0875500..0280b6e9 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -330,6 +330,7 @@ public CompletableFuture rm(Cid cid) { PresignedUrl delUrl = S3Request.preSignDelete(folder + hashToKey(cid), S3AdminRequests.asAwsDate(ZonedDateTime.now()), host, region, accessKeyId, secretKey, useHttps, hasher).join(); HttpUtil.delete(delUrl.base, delUrl.fields); + blockMetadata.remove(cid); return CompletableFuture.completedFuture(true); } catch (Exception e) { throw new RuntimeException(e); diff --git a/src/test/java/org/peergos/S3BlockStoreTest.java b/src/test/java/org/peergos/S3BlockStoreTest.java index 8dadab11..3bd2350a 100644 --- a/src/test/java/org/peergos/S3BlockStoreTest.java +++ b/src/test/java/org/peergos/S3BlockStoreTest.java @@ -4,9 +4,13 @@ import io.ipfs.multihash.Multihash; import org.junit.*; import org.peergos.blockstore.metadatadb.BlockMetadataStore; +import org.peergos.blockstore.metadatadb.JdbcBlockMetadataStore; import org.peergos.blockstore.metadatadb.RamBlockMetadataStore; +import org.peergos.blockstore.metadatadb.sql.SqliteCommands; import org.peergos.blockstore.s3.S3Blockstore; +import org.peergos.util.Sqlite; +import java.sql.Connection; import java.util.*; /* @@ -22,7 +26,18 @@ public class S3BlockStoreTest { @Test - public void testFileStore() { + public void testS3FileStoreWithRamMetaDataStore() { + BlockMetadataStore metadata = new RamBlockMetadataStore(); + testFileStore(metadata); + } + @Test + public void testS3FileStoreWithJDBCMetaDataStore() throws Exception { + Connection instance = new Sqlite.UncloseableConnection(Sqlite.build(":memory:")); + BlockMetadataStore metadata = new JdbcBlockMetadataStore(() -> instance, new SqliteCommands()); + testFileStore(metadata); + } + + public void testFileStore(BlockMetadataStore metadata) { Map params = new HashMap<>(); params.put("type", "s3ds"); params.put("region", "local"); @@ -32,13 +47,16 @@ public void testFileStore() { params.put("accessKey", "test"); params.put("secretKey", "testdslocal"); - BlockMetadataStore metadata = new RamBlockMetadataStore(); S3Blockstore bs = new S3Blockstore(params, metadata); + String msg = "hello world!"; byte[] block = msg.getBytes(); Cid.Codec codec = Cid.Codec.Raw; Cid cid = new Cid(1, codec, Multihash.Type.sha2_256, Hash.sha256(block)); + //clean up from any previous failed test + bs.rm(cid).join(); + boolean found = bs.has(cid).join(); Assert.assertTrue("Found cid", !found); From 96241df6461065d25551378bbc5f421de678523e Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 2 Oct 2023 14:42:00 +0100 Subject: [PATCH 034/218] replace sqlite/postgres with H2 --- pom.xml | 15 - src/main/java/org/peergos/EmbeddedIpfs.java | 20 ++ src/main/java/org/peergos/Nabu.java | 4 +- .../blockstore/metadatadb/Builder.java | 59 ---- .../blockstore/metadatadb/sql/H2Commands.java | 24 ++ .../metadatadb/sql/PostgresCommands.java | 51 --- .../metadatadb/sql/SqlSupplier.java | 9 - .../metadatadb/sql/SqliteCommands.java | 25 -- .../metadatadb/sql/UncloseableConnection.java | 285 ++++++++++++++++ src/main/java/org/peergos/util/Sqlite.java | 306 ------------------ .../java/org/peergos/S3BlockStoreTest.java | 12 +- 11 files changed, 340 insertions(+), 470 deletions(-) delete mode 100644 src/main/java/org/peergos/blockstore/metadatadb/Builder.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/sql/H2Commands.java delete mode 100644 src/main/java/org/peergos/blockstore/metadatadb/sql/PostgresCommands.java create mode 100644 src/main/java/org/peergos/blockstore/metadatadb/sql/UncloseableConnection.java delete mode 100644 src/main/java/org/peergos/util/Sqlite.java diff --git a/pom.xml b/pom.xml index c90c9f4d..f623d6d2 100644 --- a/pom.xml +++ b/pom.xml @@ -220,20 +220,5 @@ simpleclient_httpserver 0.16.0 - - com.zaxxer - HikariCP - 5.0.1 - - - org.postgresql - postgresql - 42.6.0 - - - org.xerial - sqlite-jdbc - 3.43.0.0 - \ No newline at end of file diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 2a934e55..93a823f3 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -8,7 +8,10 @@ import io.libp2p.protocol.*; import org.peergos.blockstore.*; import org.peergos.blockstore.metadatadb.BlockMetadataStore; +import org.peergos.blockstore.metadatadb.JdbcBlockMetadataStore; import org.peergos.blockstore.metadatadb.MetadataCachingStorage; +import org.peergos.blockstore.metadatadb.sql.SqliteCommands; +import org.peergos.blockstore.metadatadb.sql.UncloseableConnection; import org.peergos.blockstore.s3.S3Blockstore; import org.peergos.config.*; import org.peergos.net.ConnectionException; @@ -21,6 +24,9 @@ import org.peergos.util.Logging; import java.nio.file.*; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; import java.util.*; import java.util.concurrent.*; import java.util.logging.*; @@ -115,6 +121,20 @@ public CompletableFuture stop() throws Exception { return node != null ? node.stop() : CompletableFuture.completedFuture(null); } + public static BlockMetadataStore buildBlockMetadata(Args a) { + try { + //see https://www.h2database.com/html/features.html#compatibility for the extra params to support + // compatibility mode. This is required for 'ON CONFLICT DO NOTHING aka INSERT OR IGNORE INTO' + Path metadataPath = a.fromIPFSDir("nabu-block-metadata-sql-file", "nabu-blockmetadata.sql"); + java.sql.Connection h2Instance = DriverManager.getConnection("jdbc:h2:" + + metadataPath.toString() + ";MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH"); + Connection instance = new UncloseableConnection(h2Instance); + instance.setAutoCommit(true); + return new JdbcBlockMetadataStore(() -> instance, new SqliteCommands()); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMetadataStore meta) { Blockstore blocks = null; if (config.datastore.blockMount.prefix.equals("flatfs.datastore")) { diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 4ee03e5d..1d67ae6c 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -3,7 +3,6 @@ import com.sun.net.httpserver.HttpServer; import io.ipfs.multiaddr.MultiAddress; import org.peergos.blockstore.metadatadb.BlockMetadataStore; -import org.peergos.blockstore.metadatadb.Builder; import org.peergos.config.*; import org.peergos.net.APIHandler; import org.peergos.net.HttpProxyHandler; @@ -26,6 +25,7 @@ import java.util.logging.Logger; import static org.peergos.EmbeddedIpfs.buildBlockStore; +import static org.peergos.EmbeddedIpfs.buildBlockMetadata; public class Nabu { @@ -51,7 +51,7 @@ public Nabu(Args args) throws Exception { Path datastorePath = ipfsPath.resolve("datastore").resolve("h2.datastore"); DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toAbsolutePath().toString()); - BlockMetadataStore meta = Builder.buildBlockMetadata(args); + BlockMetadataStore meta = buildBlockMetadata(args); EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, buildBlockStore(config, ipfsPath, meta), config.addresses.getSwarmAddresses(), diff --git a/src/main/java/org/peergos/blockstore/metadatadb/Builder.java b/src/main/java/org/peergos/blockstore/metadatadb/Builder.java deleted file mode 100644 index 09d7253f..00000000 --- a/src/main/java/org/peergos/blockstore/metadatadb/Builder.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.peergos.blockstore.metadatadb; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.peergos.Args; -import org.peergos.blockstore.metadatadb.sql.PostgresCommands; -import org.peergos.blockstore.metadatadb.sql.SqliteCommands; -import org.peergos.util.Sqlite; - -import java.io.File; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.Properties; -import java.util.function.Supplier; - -public class Builder { - - public static Supplier getPostgresConnector(Args a, String prefix) { - String postgresHost = a.getArg(prefix + "postgres.host"); - int postgresPort = a.getInt(prefix + "postgres.port", 5432); - String databaseName = a.getArg(prefix + "postgres.database", "nabu"); - String postgresUsername = a.getArg(prefix + "postgres.username"); - String postgresPassword = a.getArg(prefix + "postgres.password"); - - Properties props = new Properties(); - props.setProperty("dataSourceClassName", "org.postgresql.ds.PGSimpleDataSource"); - props.setProperty("dataSource.serverName", postgresHost); - props.setProperty("dataSource.portNumber", "" + postgresPort); - props.setProperty("dataSource.user", postgresUsername); - props.setProperty("dataSource.password", postgresPassword); - props.setProperty("dataSource.databaseName", databaseName); - HikariConfig config = new HikariConfig(props); - HikariDataSource ds = new HikariDataSource(config); - - return () -> { - try { - return ds.getConnection(); - } catch (SQLException e) { - throw new RuntimeException(e); - } - }; - } - - public static BlockMetadataStore buildBlockMetadata(Args a) { - try { - boolean usePostgres = a.getArg("block-metadata-db-type", "sqlite").equals("postgres"); - if (usePostgres) { - return new JdbcBlockMetadataStore(getPostgresConnector(a, "metadb."), new PostgresCommands()); - } else { - File metaFile = a.fromIPFSDir("block-metadata-sql-file", "blockmetadata.sql").toFile(); - Connection instance = new Sqlite.UncloseableConnection(Sqlite.build(metaFile.getPath())); - return new JdbcBlockMetadataStore(() -> instance, new SqliteCommands()); - } - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/H2Commands.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/H2Commands.java new file mode 100644 index 00000000..5d98f384 --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/H2Commands.java @@ -0,0 +1,24 @@ +package org.peergos.blockstore.metadatadb.sql; + +public class H2Commands implements SqlSupplier { + + @Override + public String vacuumCommand() { + return ""; + } + + @Override + public String addMetadataCommand() { + return "INSERT INTO blockmetadata (cid, size, links) VALUES(?, ?, ?) ON CONFLICT DO NOTHING;"; + } + + @Override + public String getByteArrayType() { + return "OBJECT"; + } + + @Override + public String sqlInteger() { + return "BIGINT"; + } +} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/PostgresCommands.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/PostgresCommands.java deleted file mode 100644 index 7db0e08e..00000000 --- a/src/main/java/org/peergos/blockstore/metadatadb/sql/PostgresCommands.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.peergos.blockstore.metadatadb.sql; - -public class PostgresCommands implements SqlSupplier { - - @Override - public String vacuumCommand() { - return ""; - } - - @Override - public String listTablesCommand() { - return "SELECT tablename FROM pg_catalog.pg_tables " + - "WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema';"; - } - - @Override - public String tableExistsCommand() { - return "SELECT table_name FROM information_schema.tables WHERE table_schema LIKE 'public' AND table_type LIKE 'BASE TABLE' AND table_name = ?;"; - } - - @Override - public String addMetadataCommand() { - return "INSERT INTO blockmetadata (cid, size, links) VALUES(?, ?, ?) ON CONFLICT DO NOTHING;"; - } - - @Override - public String ensureColumnExistsCommand(String table, String column, String type) { - return "ALTER TABLE " + table + " ADD COLUMN IF NOT EXISTS " + column + " " + type + ";"; - } - - - @Override - public String insertOrIgnoreCommand(String prefix, String suffix) { - return prefix + suffix + " ON CONFLICT DO NOTHING;"; - } - - @Override - public String getByteArrayType() { - return "BYTEA"; - } - - @Override - public String getSerialIdType() { - return "SERIAL"; - } - - @Override - public String sqlInteger() { - return "BIGINT"; - } -} diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java index 31402d2b..1c3d01ff 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java @@ -6,18 +6,11 @@ public interface SqlSupplier { - String listTablesCommand(); - - String tableExistsCommand(); - String getByteArrayType(); - String getSerialIdType(); String sqlInteger(); - String ensureColumnExistsCommand(String table, String column, String type); - String addMetadataCommand(); String vacuumCommand(); @@ -28,8 +21,6 @@ default String createBlockMetadataStoreTableCommand() { "links " + getByteArrayType() + " not null);"; } - String insertOrIgnoreCommand(String prefix, String suffix); - default void createTable(String sqlTableCreate, Connection conn) throws SQLException { Statement createStmt = conn.createStatement(); createStmt.executeUpdate(sqlTableCreate); diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java index 12711b51..38df3133 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java @@ -7,41 +7,16 @@ public String vacuumCommand() { return "VACUUM;"; } - @Override - public String listTablesCommand() { - return "SELECT NAME FROM sqlite_master WHERE type='table';"; - } - - @Override - public String tableExistsCommand() { - return "SELECT name FROM sqlite_master WHERE type='table' AND name=?;"; - } - @Override public String addMetadataCommand() { return "INSERT OR IGNORE INTO blockmetadata (cid, size, links) VALUES(?, ?, ?);"; } - @Override - public String ensureColumnExistsCommand(String table, String column, String type) { - return "ALTER TABLE " + table + " ADD COLUMN " + column + " " + type + ";"; - } - - @Override - public String insertOrIgnoreCommand(String prefix, String suffix) { - return prefix + "OR IGNORE " + suffix + ";"; - } - @Override public String getByteArrayType() { return "blob"; } - @Override - public String getSerialIdType() { - return "INTEGER"; - } - @Override public String sqlInteger() { return "INTEGER"; diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/UncloseableConnection.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/UncloseableConnection.java new file mode 100644 index 00000000..c5e7222f --- /dev/null +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/UncloseableConnection.java @@ -0,0 +1,285 @@ +package org.peergos.blockstore.metadatadb.sql; + +import java.sql.*; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +public class UncloseableConnection implements Connection { + + private final Connection target; + + public UncloseableConnection(Connection target) { + this.target = target; + } + + @Override + public Statement createStatement() throws SQLException { + return target.createStatement(); + } + + @Override + public PreparedStatement prepareStatement(String s) throws SQLException { + return target.prepareStatement(s); + } + + @Override + public CallableStatement prepareCall(String s) throws SQLException { + return target.prepareCall(s); + } + + @Override + public String nativeSQL(String s) throws SQLException { + return target.nativeSQL(s); + } + + @Override + public void setAutoCommit(boolean b) throws SQLException { + target.setAutoCommit(b); + } + + @Override + public boolean getAutoCommit() throws SQLException { + return target.getAutoCommit(); + } + + @Override + public void commit() throws SQLException { + target.commit(); + } + + @Override + public void rollback() throws SQLException { + target.rollback(); + } + + @Override + public void close() throws SQLException { + // Do nothing + } + + @Override + public boolean isClosed() throws SQLException { + return false; + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return target.getMetaData(); + } + + @Override + public void setReadOnly(boolean b) throws SQLException { + target.setReadOnly(b); + } + + @Override + public boolean isReadOnly() throws SQLException { + return target.isReadOnly(); + } + + @Override + public void setCatalog(String s) throws SQLException { + target.setCatalog(s); + } + + @Override + public String getCatalog() throws SQLException { + return target.getCatalog(); + } + + @Override + public void setTransactionIsolation(int i) throws SQLException { + target.setTransactionIsolation(i); + } + + @Override + public int getTransactionIsolation() throws SQLException { + return target.getTransactionIsolation(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return target.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + target.clearWarnings(); + } + + @Override + public Statement createStatement(int i, int i1) throws SQLException { + return target.createStatement(i, i1); + } + + @Override + public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException { + return target.prepareStatement(s, i, i1); + } + + @Override + public CallableStatement prepareCall(String s, int i, int i1) throws SQLException { + return target.prepareCall(s, i, i1); + } + + @Override + public Map> getTypeMap() throws SQLException { + return target.getTypeMap(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + target.setTypeMap(map); + } + + @Override + public void setHoldability(int i) throws SQLException { + target.setHoldability(i); + } + + @Override + public int getHoldability() throws SQLException { + return target.getHoldability(); + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return target.setSavepoint(); + } + + @Override + public Savepoint setSavepoint(String s) throws SQLException { + return target.setSavepoint(s); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + target.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + target.releaseSavepoint(savepoint); + } + + @Override + public Statement createStatement(int i, int i1, int i2) throws SQLException { + return target.createStatement(i, i1, i2); + } + + @Override + public PreparedStatement prepareStatement(String s, int i, int i1, int i2) throws SQLException { + return target.prepareStatement(s, i, i1, i2); + } + + @Override + public CallableStatement prepareCall(String s, int i, int i1, int i2) throws SQLException { + return target.prepareCall(s, i, i1, i2); + } + + @Override + public PreparedStatement prepareStatement(String s, int i) throws SQLException { + return target.prepareStatement(s, i); + } + + @Override + public PreparedStatement prepareStatement(String s, int[] ints) throws SQLException { + return target.prepareStatement(s, ints); + } + + @Override + public PreparedStatement prepareStatement(String s, String[] strings) throws SQLException { + return target.prepareStatement(s, strings); + } + + @Override + public Clob createClob() throws SQLException { + return target.createClob(); + } + + @Override + public Blob createBlob() throws SQLException { + return target.createBlob(); + } + + @Override + public NClob createNClob() throws SQLException { + return target.createNClob(); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return target.createSQLXML(); + } + + @Override + public boolean isValid(int i) throws SQLException { + return target.isValid(i); + } + + @Override + public void setClientInfo(String s, String s1) throws SQLClientInfoException { + target.setClientInfo(s, s1); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + target.setClientInfo(properties); + } + + @Override + public String getClientInfo(String s) throws SQLException { + return target.getClientInfo(s); + } + + @Override + public Properties getClientInfo() throws SQLException { + return target.getClientInfo(); + } + + @Override + public Array createArrayOf(String s, Object[] objects) throws SQLException { + return target.createArrayOf(s, objects); + } + + @Override + public Struct createStruct(String s, Object[] objects) throws SQLException { + return target.createStruct(s, objects); + } + + @Override + public void setSchema(String s) throws SQLException { + target.setSchema(s); + } + + @Override + public String getSchema() throws SQLException { + return target.getSchema(); + } + + @Override + public void abort(Executor executor) throws SQLException { + target.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int i) throws SQLException { + target.setNetworkTimeout(executor, i); + } + + @Override + public int getNetworkTimeout() throws SQLException { + return target.getNetworkTimeout(); + } + + @Override + public T unwrap(Class aClass) throws SQLException { + return target.unwrap(aClass); + } + + @Override + public boolean isWrapperFor(Class aClass) throws SQLException { + return target.isWrapperFor(aClass); + } +} diff --git a/src/main/java/org/peergos/util/Sqlite.java b/src/main/java/org/peergos/util/Sqlite.java deleted file mode 100644 index 4fe117ce..00000000 --- a/src/main/java/org/peergos/util/Sqlite.java +++ /dev/null @@ -1,306 +0,0 @@ -package org.peergos.util; - -import org.peergos.Args; -import org.sqlite.*; - -import java.sql.*; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Executor; - -public class Sqlite { - - public static Connection build(String dbPath) throws SQLException { - String url = "jdbc:sqlite:"+dbPath; - SQLiteDataSource dc = new SQLiteDataSource(); - dc.setUrl(url); - - Connection conn = dc.getConnection(); - conn.setAutoCommit(true); - return conn; - } - - public static String getDbPath(Args a, String type) { - String sqlFile = a.getArg(type); - return sqlFile.equals(":memory:") ? sqlFile : a.fromIPFSDir(type).toString(); - } - - public static class UncloseableConnection implements Connection { - - private final Connection target; - - public UncloseableConnection(Connection target) { - this.target = target; - } - - @Override - public Statement createStatement() throws SQLException { - return target.createStatement(); - } - - @Override - public PreparedStatement prepareStatement(String s) throws SQLException { - return target.prepareStatement(s); - } - - @Override - public CallableStatement prepareCall(String s) throws SQLException { - return target.prepareCall(s); - } - - @Override - public String nativeSQL(String s) throws SQLException { - return target.nativeSQL(s); - } - - @Override - public void setAutoCommit(boolean b) throws SQLException { - target.setAutoCommit(b); - } - - @Override - public boolean getAutoCommit() throws SQLException { - return target.getAutoCommit(); - } - - @Override - public void commit() throws SQLException { - target.commit(); - } - - @Override - public void rollback() throws SQLException { - target.rollback(); - } - - @Override - public void close() throws SQLException { - // Do nothing - } - - @Override - public boolean isClosed() throws SQLException { - return false; - } - - @Override - public DatabaseMetaData getMetaData() throws SQLException { - return target.getMetaData(); - } - - @Override - public void setReadOnly(boolean b) throws SQLException { - target.setReadOnly(b); - } - - @Override - public boolean isReadOnly() throws SQLException { - return target.isReadOnly(); - } - - @Override - public void setCatalog(String s) throws SQLException { - target.setCatalog(s); - } - - @Override - public String getCatalog() throws SQLException { - return target.getCatalog(); - } - - @Override - public void setTransactionIsolation(int i) throws SQLException { - target.setTransactionIsolation(i); - } - - @Override - public int getTransactionIsolation() throws SQLException { - return target.getTransactionIsolation(); - } - - @Override - public SQLWarning getWarnings() throws SQLException { - return target.getWarnings(); - } - - @Override - public void clearWarnings() throws SQLException { - target.clearWarnings(); - } - - @Override - public Statement createStatement(int i, int i1) throws SQLException { - return target.createStatement(i, i1); - } - - @Override - public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException { - return target.prepareStatement(s, i, i1); - } - - @Override - public CallableStatement prepareCall(String s, int i, int i1) throws SQLException { - return target.prepareCall(s, i, i1); - } - - @Override - public Map> getTypeMap() throws SQLException { - return target.getTypeMap(); - } - - @Override - public void setTypeMap(Map> map) throws SQLException { - target.setTypeMap(map); - } - - @Override - public void setHoldability(int i) throws SQLException { - target.setHoldability(i); - } - - @Override - public int getHoldability() throws SQLException { - return target.getHoldability(); - } - - @Override - public Savepoint setSavepoint() throws SQLException { - return target.setSavepoint(); - } - - @Override - public Savepoint setSavepoint(String s) throws SQLException { - return target.setSavepoint(s); - } - - @Override - public void rollback(Savepoint savepoint) throws SQLException { - target.rollback(savepoint); - } - - @Override - public void releaseSavepoint(Savepoint savepoint) throws SQLException { - target.releaseSavepoint(savepoint); - } - - @Override - public Statement createStatement(int i, int i1, int i2) throws SQLException { - return target.createStatement(i, i1, i2); - } - - @Override - public PreparedStatement prepareStatement(String s, int i, int i1, int i2) throws SQLException { - return target.prepareStatement(s, i, i1, i2); - } - - @Override - public CallableStatement prepareCall(String s, int i, int i1, int i2) throws SQLException { - return target.prepareCall(s, i, i1, i2); - } - - @Override - public PreparedStatement prepareStatement(String s, int i) throws SQLException { - return target.prepareStatement(s, i); - } - - @Override - public PreparedStatement prepareStatement(String s, int[] ints) throws SQLException { - return target.prepareStatement(s, ints); - } - - @Override - public PreparedStatement prepareStatement(String s, String[] strings) throws SQLException { - return target.prepareStatement(s, strings); - } - - @Override - public Clob createClob() throws SQLException { - return target.createClob(); - } - - @Override - public Blob createBlob() throws SQLException { - return target.createBlob(); - } - - @Override - public NClob createNClob() throws SQLException { - return target.createNClob(); - } - - @Override - public SQLXML createSQLXML() throws SQLException { - return target.createSQLXML(); - } - - @Override - public boolean isValid(int i) throws SQLException { - return target.isValid(i); - } - - @Override - public void setClientInfo(String s, String s1) throws SQLClientInfoException { - target.setClientInfo(s, s1); - } - - @Override - public void setClientInfo(Properties properties) throws SQLClientInfoException { - target.setClientInfo(properties); - } - - @Override - public String getClientInfo(String s) throws SQLException { - return target.getClientInfo(s); - } - - @Override - public Properties getClientInfo() throws SQLException { - return target.getClientInfo(); - } - - @Override - public Array createArrayOf(String s, Object[] objects) throws SQLException { - return target.createArrayOf(s, objects); - } - - @Override - public Struct createStruct(String s, Object[] objects) throws SQLException { - return target.createStruct(s, objects); - } - - @Override - public void setSchema(String s) throws SQLException { - target.setSchema(s); - } - - @Override - public String getSchema() throws SQLException { - return target.getSchema(); - } - - @Override - public void abort(Executor executor) throws SQLException { - target.abort(executor); - } - - @Override - public void setNetworkTimeout(Executor executor, int i) throws SQLException { - target.setNetworkTimeout(executor, i); - } - - @Override - public int getNetworkTimeout() throws SQLException { - return target.getNetworkTimeout(); - } - - @Override - public T unwrap(Class aClass) throws SQLException { - return target.unwrap(aClass); - } - - @Override - public boolean isWrapperFor(Class aClass) throws SQLException { - return target.isWrapperFor(aClass); - } - } -} diff --git a/src/test/java/org/peergos/S3BlockStoreTest.java b/src/test/java/org/peergos/S3BlockStoreTest.java index 3bd2350a..bf2d5235 100644 --- a/src/test/java/org/peergos/S3BlockStoreTest.java +++ b/src/test/java/org/peergos/S3BlockStoreTest.java @@ -6,11 +6,13 @@ import org.peergos.blockstore.metadatadb.BlockMetadataStore; import org.peergos.blockstore.metadatadb.JdbcBlockMetadataStore; import org.peergos.blockstore.metadatadb.RamBlockMetadataStore; +import org.peergos.blockstore.metadatadb.sql.H2Commands; import org.peergos.blockstore.metadatadb.sql.SqliteCommands; +import org.peergos.blockstore.metadatadb.sql.UncloseableConnection; import org.peergos.blockstore.s3.S3Blockstore; -import org.peergos.util.Sqlite; import java.sql.Connection; +import java.sql.DriverManager; import java.util.*; /* @@ -32,8 +34,12 @@ public void testS3FileStoreWithRamMetaDataStore() { } @Test public void testS3FileStoreWithJDBCMetaDataStore() throws Exception { - Connection instance = new Sqlite.UncloseableConnection(Sqlite.build(":memory:")); - BlockMetadataStore metadata = new JdbcBlockMetadataStore(() -> instance, new SqliteCommands()); + Connection h2Instance = DriverManager.getConnection("jdbc:h2:" + + "mem:" + ";MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH"); + Connection instance = new UncloseableConnection(h2Instance); + + instance.setAutoCommit(true); + BlockMetadataStore metadata = new JdbcBlockMetadataStore(() -> instance, new H2Commands()); testFileStore(metadata); } From 56478a3b78d3b9bafff1cf5f4db949878bee8c5d Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 2 Oct 2023 14:55:17 +0100 Subject: [PATCH 035/218] rename classes to be consistent --- src/main/java/org/peergos/EmbeddedIpfs.java | 8 +++---- ...ge.java => CachingBlockMetadataStore.java} | 4 ++-- .../metadatadb/JdbcBlockMetadataStore.java | 8 +++---- ...ier.java => BlockMetadataSqlSupplier.java} | 2 +- ...ands.java => H2BlockMetadataCommands.java} | 2 +- .../metadatadb/sql/SqliteCommands.java | 24 ------------------- .../java/org/peergos/S3BlockStoreTest.java | 5 ++-- 7 files changed, 14 insertions(+), 39 deletions(-) rename src/main/java/org/peergos/blockstore/metadatadb/{MetadataCachingStorage.java => CachingBlockMetadataStore.java} (95%) rename src/main/java/org/peergos/blockstore/metadatadb/sql/{SqlSupplier.java => BlockMetadataSqlSupplier.java} (94%) rename src/main/java/org/peergos/blockstore/metadatadb/sql/{H2Commands.java => H2BlockMetadataCommands.java} (85%) delete mode 100644 src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 93a823f3..00f6439c 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -9,8 +9,8 @@ import org.peergos.blockstore.*; import org.peergos.blockstore.metadatadb.BlockMetadataStore; import org.peergos.blockstore.metadatadb.JdbcBlockMetadataStore; -import org.peergos.blockstore.metadatadb.MetadataCachingStorage; -import org.peergos.blockstore.metadatadb.sql.SqliteCommands; +import org.peergos.blockstore.metadatadb.CachingBlockMetadataStore; +import org.peergos.blockstore.metadatadb.sql.H2BlockMetadataCommands; import org.peergos.blockstore.metadatadb.sql.UncloseableConnection; import org.peergos.blockstore.s3.S3Blockstore; import org.peergos.config.*; @@ -130,7 +130,7 @@ public static BlockMetadataStore buildBlockMetadata(Args a) { metadataPath.toString() + ";MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH"); Connection instance = new UncloseableConnection(h2Instance); instance.setAutoCommit(true); - return new JdbcBlockMetadataStore(() -> instance, new SqliteCommands()); + return new JdbcBlockMetadataStore(() -> instance, new H2BlockMetadataCommands()); } catch (SQLException e) { throw new RuntimeException(e); } @@ -160,7 +160,7 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMeta blockStore : new TypeLimitedBlockstore(blockStore, config.datastore.allowedCodecs.codecs); return config.datastore.blockMount.prefix.equals("s3.datastore") ? filteredBlockStore - : new MetadataCachingStorage(filteredBlockStore, meta); + : new CachingBlockMetadataStore(filteredBlockStore, meta); } public static EmbeddedIpfs build(RecordStore records, diff --git a/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java similarity index 95% rename from src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java rename to src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java index 198dd777..cc6f5438 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/MetadataCachingStorage.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java @@ -11,12 +11,12 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; -public class MetadataCachingStorage implements Blockstore { +public class CachingBlockMetadataStore implements Blockstore { private final Blockstore target; private final BlockMetadataStore metadata; - public MetadataCachingStorage(Blockstore target, BlockMetadataStore metadata) { + public CachingBlockMetadataStore(Blockstore target, BlockMetadataStore metadata) { this.target = target; this.metadata = metadata; } diff --git a/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java index 0a7d8358..be84b0ec 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/JdbcBlockMetadataStore.java @@ -2,7 +2,7 @@ import io.ipfs.cid.Cid; -import org.peergos.blockstore.metadatadb.sql.SqlSupplier; +import org.peergos.blockstore.metadatadb.sql.BlockMetadataSqlSupplier; import org.peergos.cbor.CborObject; import org.peergos.util.Logging; @@ -27,9 +27,9 @@ public class JdbcBlockMetadataStore implements BlockMetadataStore { private static final String LIST = "SELECT cid FROM blockmetadata;"; private static final String SIZE = "SELECT COUNT(*) FROM blockmetadata;"; private Supplier conn; - private final SqlSupplier commands; + private final BlockMetadataSqlSupplier commands; - public JdbcBlockMetadataStore(Supplier conn, SqlSupplier commands) { + public JdbcBlockMetadataStore(Supplier conn, BlockMetadataSqlSupplier commands) { this.conn = conn; this.commands = commands; init(commands); @@ -52,7 +52,7 @@ private Connection getConnection(boolean autocommit, boolean serializable) { } } - private synchronized void init(SqlSupplier commands) { + private synchronized void init(BlockMetadataSqlSupplier commands) { try (Connection conn = getConnection()) { commands.createTable(commands.createBlockMetadataStoreTableCommand(), conn); } catch (Exception e) { diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/BlockMetadataSqlSupplier.java similarity index 94% rename from src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java rename to src/main/java/org/peergos/blockstore/metadatadb/sql/BlockMetadataSqlSupplier.java index 1c3d01ff..49e93a2b 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqlSupplier.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/BlockMetadataSqlSupplier.java @@ -4,7 +4,7 @@ import java.sql.SQLException; import java.sql.Statement; -public interface SqlSupplier { +public interface BlockMetadataSqlSupplier { String getByteArrayType(); diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/H2Commands.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/H2BlockMetadataCommands.java similarity index 85% rename from src/main/java/org/peergos/blockstore/metadatadb/sql/H2Commands.java rename to src/main/java/org/peergos/blockstore/metadatadb/sql/H2BlockMetadataCommands.java index 5d98f384..e74e5f3d 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/sql/H2Commands.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/sql/H2BlockMetadataCommands.java @@ -1,6 +1,6 @@ package org.peergos.blockstore.metadatadb.sql; -public class H2Commands implements SqlSupplier { +public class H2BlockMetadataCommands implements BlockMetadataSqlSupplier { @Override public String vacuumCommand() { diff --git a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java b/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java deleted file mode 100644 index 38df3133..00000000 --- a/src/main/java/org/peergos/blockstore/metadatadb/sql/SqliteCommands.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.peergos.blockstore.metadatadb.sql; - -public class SqliteCommands implements SqlSupplier { - - @Override - public String vacuumCommand() { - return "VACUUM;"; - } - - @Override - public String addMetadataCommand() { - return "INSERT OR IGNORE INTO blockmetadata (cid, size, links) VALUES(?, ?, ?);"; - } - - @Override - public String getByteArrayType() { - return "blob"; - } - - @Override - public String sqlInteger() { - return "INTEGER"; - } -} diff --git a/src/test/java/org/peergos/S3BlockStoreTest.java b/src/test/java/org/peergos/S3BlockStoreTest.java index bf2d5235..f462b8d5 100644 --- a/src/test/java/org/peergos/S3BlockStoreTest.java +++ b/src/test/java/org/peergos/S3BlockStoreTest.java @@ -6,8 +6,7 @@ import org.peergos.blockstore.metadatadb.BlockMetadataStore; import org.peergos.blockstore.metadatadb.JdbcBlockMetadataStore; import org.peergos.blockstore.metadatadb.RamBlockMetadataStore; -import org.peergos.blockstore.metadatadb.sql.H2Commands; -import org.peergos.blockstore.metadatadb.sql.SqliteCommands; +import org.peergos.blockstore.metadatadb.sql.H2BlockMetadataCommands; import org.peergos.blockstore.metadatadb.sql.UncloseableConnection; import org.peergos.blockstore.s3.S3Blockstore; @@ -39,7 +38,7 @@ public void testS3FileStoreWithJDBCMetaDataStore() throws Exception { Connection instance = new UncloseableConnection(h2Instance); instance.setAutoCommit(true); - BlockMetadataStore metadata = new JdbcBlockMetadataStore(() -> instance, new H2Commands()); + BlockMetadataStore metadata = new JdbcBlockMetadataStore(() -> instance, new H2BlockMetadataCommands()); testFileStore(metadata); } From 76625fc900c17b77d5aa30ac3f46bcca23881414 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 6 Oct 2023 09:26:44 +0100 Subject: [PATCH 036/218] make h2 db path absolute --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 00f6439c..46649643 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -127,7 +127,7 @@ public static BlockMetadataStore buildBlockMetadata(Args a) { // compatibility mode. This is required for 'ON CONFLICT DO NOTHING aka INSERT OR IGNORE INTO' Path metadataPath = a.fromIPFSDir("nabu-block-metadata-sql-file", "nabu-blockmetadata.sql"); java.sql.Connection h2Instance = DriverManager.getConnection("jdbc:h2:" + - metadataPath.toString() + ";MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH"); + metadataPath.toAbsolutePath() + ";MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH"); Connection instance = new UncloseableConnection(h2Instance); instance.setAutoCommit(true); return new JdbcBlockMetadataStore(() -> instance, new H2BlockMetadataCommands()); From b46bc122360c0ff54e36566ea55d23add3c91e91 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 7 Oct 2023 20:11:22 +0100 Subject: [PATCH 037/218] Make sure we always look up a node on the dht by the raw peerid --- src/main/java/org/peergos/EmbeddedIpfs.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index ce83efd3..a0bd7958 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -173,14 +173,15 @@ public static EmbeddedIpfs build(RecordStore records, public static Multiaddr[] getAddresses(Host node, Kademlia dht, Multihash targetNodeId) throws ConnectionException { AddressBook addressBook = node.getAddressBook(); - PeerId peerId = PeerId.fromBase58(targetNodeId.bareMultihash().toBase58()); + Multihash targetPeerId = targetNodeId.bareMultihash(); + PeerId peerId = PeerId.fromBase58(targetPeerId.toBase58()); Optional targetAddressesOpt = addressBook.get(peerId).join().stream().findFirst(); Multiaddr[] allAddresses = null; if (targetAddressesOpt.isEmpty()) { - List closestPeers = dht.findClosestPeers(targetNodeId, 1, node); - Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetNodeId)).findFirst(); + List closestPeers = dht.findClosestPeers(targetPeerId, 1, node); + Optional matching = closestPeers.stream().filter(p -> p.peerId.equals(targetPeerId)).findFirst(); if (matching.isEmpty()) { - throw new ConnectionException("Target not found: " + targetNodeId); + throw new ConnectionException("Target not found: " + targetPeerId); } PeerAddresses peer = matching.get(); allAddresses = peer.addresses.stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); From edcf293d7461dc71c7367ad3fe522cd3a65175b7 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Tue, 10 Oct 2023 09:58:05 +0100 Subject: [PATCH 038/218] copy array response in getAddrs --- src/main/java/org/peergos/RamAddressBook.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/RamAddressBook.java b/src/main/java/org/peergos/RamAddressBook.java index 1f6bef84..59c24ca0 100644 --- a/src/main/java/org/peergos/RamAddressBook.java +++ b/src/main/java/org/peergos/RamAddressBook.java @@ -25,7 +25,7 @@ public CompletableFuture addAddrs(@NotNull PeerId peerId, long ttl, @NotNu @NotNull @Override public CompletableFuture> getAddrs(@NotNull PeerId peerId) { - return CompletableFuture.completedFuture(addresses.getOrDefault(peerId, Collections.emptySet())); + return CompletableFuture.completedFuture(new ArrayList(addresses.getOrDefault(peerId, Collections.emptySet()))); } @NotNull From 1e74997e65b101b99ea22d2792bca8c1c5646b4a Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Tue, 10 Oct 2023 16:11:58 +0100 Subject: [PATCH 039/218] move RAMBlockMetadataStore to test package --- .../java/org/peergos}/RamBlockMetadataStore.java | 4 +++- src/test/java/org/peergos/S3BlockStoreTest.java | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) rename src/{main/java/org/peergos/blockstore/metadatadb => test/java/org/peergos}/RamBlockMetadataStore.java (88%) diff --git a/src/main/java/org/peergos/blockstore/metadatadb/RamBlockMetadataStore.java b/src/test/java/org/peergos/RamBlockMetadataStore.java similarity index 88% rename from src/main/java/org/peergos/blockstore/metadatadb/RamBlockMetadataStore.java rename to src/test/java/org/peergos/RamBlockMetadataStore.java index 58836457..506da984 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/RamBlockMetadataStore.java +++ b/src/test/java/org/peergos/RamBlockMetadataStore.java @@ -1,6 +1,8 @@ -package org.peergos.blockstore.metadatadb; +package org.peergos; import io.ipfs.cid.Cid; +import org.peergos.blockstore.metadatadb.BlockMetadata; +import org.peergos.blockstore.metadatadb.BlockMetadataStore; import java.util.HashMap; import java.util.Map; diff --git a/src/test/java/org/peergos/S3BlockStoreTest.java b/src/test/java/org/peergos/S3BlockStoreTest.java index f462b8d5..80f58886 100644 --- a/src/test/java/org/peergos/S3BlockStoreTest.java +++ b/src/test/java/org/peergos/S3BlockStoreTest.java @@ -5,7 +5,6 @@ import org.junit.*; import org.peergos.blockstore.metadatadb.BlockMetadataStore; import org.peergos.blockstore.metadatadb.JdbcBlockMetadataStore; -import org.peergos.blockstore.metadatadb.RamBlockMetadataStore; import org.peergos.blockstore.metadatadb.sql.H2BlockMetadataCommands; import org.peergos.blockstore.metadatadb.sql.UncloseableConnection; import org.peergos.blockstore.s3.S3Blockstore; From 7f373572eb1b1dd2597c25ed8f1242af58832c50 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Tue, 10 Oct 2023 16:15:51 +0100 Subject: [PATCH 040/218] add check to extractMetadata --- .../org/peergos/blockstore/metadatadb/BlockMetadataStore.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadataStore.java index e1bbde68..80edf325 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadataStore.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/BlockMetadataStore.java @@ -33,7 +33,7 @@ static BlockMetadata extractMetadata(Cid block, byte[] data) { if (block.codec == Cid.Codec.Raw) { BlockMetadata meta = new BlockMetadata(data.length, Collections.emptyList()); return meta; - } else { + } else if(block.codec == Cid.Codec.DagCbor){ CborObject cbor = CborObject.fromByteArray(data); List links = cbor .links().stream() @@ -41,6 +41,8 @@ static BlockMetadata extractMetadata(Cid block, byte[] data) { .collect(Collectors.toList()); BlockMetadata meta = new BlockMetadata(data.length, links); return meta; + } else { + throw new IllegalStateException("Unsupported Block type"); } } From e69bea27b845f15755d55369fd33b92b47e2e5df Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Tue, 10 Oct 2023 16:24:24 +0100 Subject: [PATCH 041/218] remove default method for getBlockMetadata --- src/main/java/org/peergos/blockstore/Blockstore.java | 5 +---- src/main/java/org/peergos/blockstore/FileBlockstore.java | 6 ++++++ .../java/org/peergos/blockstore/FilteredBlockstore.java | 7 +++++++ .../java/org/peergos/blockstore/ProvidingBlockstore.java | 6 ++++++ src/main/java/org/peergos/blockstore/RamBlockstore.java | 7 +++++++ .../java/org/peergos/blockstore/TypeLimitedBlockstore.java | 7 +++++++ 6 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/Blockstore.java b/src/main/java/org/peergos/blockstore/Blockstore.java index 42450278..41dcee9b 100644 --- a/src/main/java/org/peergos/blockstore/Blockstore.java +++ b/src/main/java/org/peergos/blockstore/Blockstore.java @@ -40,8 +40,5 @@ default Optional getBlockMetadataStore() { return Optional.empty(); } - default CompletableFuture getBlockMetadata(Cid h) { - return get(h) - .thenApply(rawOpt -> BlockMetadataStore.extractMetadata(h, rawOpt.get())); - } + CompletableFuture getBlockMetadata(Cid h); } \ No newline at end of file diff --git a/src/main/java/org/peergos/blockstore/FileBlockstore.java b/src/main/java/org/peergos/blockstore/FileBlockstore.java index 41a87c65..b34c24da 100644 --- a/src/main/java/org/peergos/blockstore/FileBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FileBlockstore.java @@ -3,6 +3,7 @@ import io.ipfs.cid.Cid; import io.ipfs.multihash.Multihash; import org.peergos.Hash; +import org.peergos.blockstore.metadatadb.BlockMetadata; import org.peergos.util.*; import java.io.*; @@ -147,4 +148,9 @@ public CompletableFuture> refs() { }).collect(Collectors.toList()); return CompletableFuture.completedFuture(cidList); } + + @Override + public CompletableFuture getBlockMetadata(Cid h) { + throw new IllegalStateException("Unsupported operation!"); + } } diff --git a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java index 330af81a..3952e431 100644 --- a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java @@ -2,6 +2,7 @@ import io.ipfs.cid.Cid; import io.ipfs.multihash.*; +import org.peergos.blockstore.metadatadb.BlockMetadata; import org.peergos.util.*; import java.util.List; @@ -67,4 +68,10 @@ public static FilteredBlockstore bloomBased(Blockstore source, double falsePosit public static FilteredBlockstore infiniBased(Blockstore source, double falsePositiveRate) { return new FilteredBlockstore(source, CidInfiniFilter.build(source, falsePositiveRate)); } + + @Override + public CompletableFuture getBlockMetadata(Cid h) { + return blocks.getBlockMetadata(h); + } + } diff --git a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java index 8f6a7602..5f108112 100644 --- a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java +++ b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java @@ -2,6 +2,7 @@ import io.ipfs.cid.*; import io.ipfs.multihash.*; +import org.peergos.blockstore.metadatadb.BlockMetadata; import java.util.*; import java.util.concurrent.*; @@ -51,4 +52,9 @@ public CompletableFuture> refs() { public CompletableFuture bloomAdd(Cid cid) { return target.bloomAdd(cid); } + + @Override + public CompletableFuture getBlockMetadata(Cid h) { + return target.getBlockMetadata(h); + } } diff --git a/src/main/java/org/peergos/blockstore/RamBlockstore.java b/src/main/java/org/peergos/blockstore/RamBlockstore.java index a3784d1b..c7f2f483 100644 --- a/src/main/java/org/peergos/blockstore/RamBlockstore.java +++ b/src/main/java/org/peergos/blockstore/RamBlockstore.java @@ -3,6 +3,7 @@ import io.ipfs.cid.*; import io.ipfs.multihash.*; import org.peergos.*; +import org.peergos.blockstore.metadatadb.BlockMetadata; import org.peergos.util.*; import java.util.*; @@ -56,4 +57,10 @@ public CompletableFuture bloomAdd(Cid cid) { public CompletableFuture> refs() { return CompletableFuture.completedFuture(new ArrayList(blocks.keySet())); } + + @Override + public CompletableFuture getBlockMetadata(Cid h) { + throw new IllegalStateException("Unsupported operation!"); + } + } diff --git a/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java b/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java index ef426e8e..623a334c 100644 --- a/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java +++ b/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java @@ -2,6 +2,7 @@ import io.ipfs.cid.Cid; import io.ipfs.multihash.*; +import org.peergos.blockstore.metadatadb.BlockMetadata; import org.peergos.util.*; import java.util.List; @@ -71,4 +72,10 @@ public CompletableFuture rm(Cid cid) { public CompletableFuture> refs() { return blocks.refs(); } + + @Override + public CompletableFuture getBlockMetadata(Cid h) { + return blocks.getBlockMetadata(h); + } + } From f5b8c0d04d1e5e331b790104476aef8d3db78ae7 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Tue, 10 Oct 2023 16:28:37 +0100 Subject: [PATCH 042/218] clean up interface --- src/main/java/org/peergos/blockstore/Blockstore.java | 4 ---- .../blockstore/metadatadb/CachingBlockMetadataStore.java | 5 ----- 2 files changed, 9 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/Blockstore.java b/src/main/java/org/peergos/blockstore/Blockstore.java index 41dcee9b..163fb8db 100644 --- a/src/main/java/org/peergos/blockstore/Blockstore.java +++ b/src/main/java/org/peergos/blockstore/Blockstore.java @@ -36,9 +36,5 @@ default Cid keyToHash(String key) { CompletableFuture bloomAdd(Cid cid); - default Optional getBlockMetadataStore() { - return Optional.empty(); - } - CompletableFuture getBlockMetadata(Cid h); } \ No newline at end of file diff --git a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java index cc6f5438..4ea2f218 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java @@ -21,11 +21,6 @@ public CachingBlockMetadataStore(Blockstore target, BlockMetadataStore metadata) this.metadata = metadata; } - @Override - public Optional getBlockMetadataStore() { - return Optional.of(metadata); - } - @Override public CompletableFuture put(byte[] block, Cid.Codec codec) { return target.put(block, codec) From c1a6bfb794f7fad4f7db893b911b22f6a7f9e009 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 10 Oct 2023 20:26:46 +0100 Subject: [PATCH 043/218] Make sure providing blockstore listing uses metadb --- src/main/java/org/peergos/EmbeddedIpfs.java | 3 +- src/main/java/org/peergos/Nabu.java | 1 + .../blockstore/ProvidingBlockstore.java | 11 +- src/test/java/org/peergos/APIServiceTest.java | 4 +- .../java/org/peergos/EmbeddedIpfsTest.java | 2 +- src/test/java/org/peergos/HandlerTest.java | 6 +- src/test/java/org/peergos/KademliaTest.java | 23 ++-- .../java/org/peergos/P2pHttpChatTest.java | 109 ++++++++++++++---- src/test/java/org/peergos/P2pHttpTest.java | 12 +- 9 files changed, 122 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index f974d632..78bce4ef 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -165,12 +165,13 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMeta public static EmbeddedIpfs build(RecordStore records, Blockstore blocks, + BlockMetadataStore metaDB, List swarmAddresses, List bootstrap, IdentitySection identity, BlockRequestAuthoriser authoriser, Optional handler) { - ProvidingBlockstore blockstore = new ProvidingBlockstore(blocks); + ProvidingBlockstore blockstore = new ProvidingBlockstore(blocks,metaDB); ProviderStore providers = new RamProviderStore(); HostBuilder builder = new HostBuilder().setIdentity(identity.privKeyProtobuf).listen(swarmAddresses); diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 1d67ae6c..1c31166e 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -54,6 +54,7 @@ public Nabu(Args args) throws Exception { BlockMetadataStore meta = buildBlockMetadata(args); EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, buildBlockStore(config, ipfsPath, meta), + meta, config.addresses.getSwarmAddresses(), config.bootstrap.getBootstrapAddresses(), config.identity, diff --git a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java index 5f108112..e9304879 100644 --- a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java +++ b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java @@ -2,18 +2,21 @@ import io.ipfs.cid.*; import io.ipfs.multihash.*; -import org.peergos.blockstore.metadatadb.BlockMetadata; +import org.peergos.blockstore.metadatadb.*; import java.util.*; import java.util.concurrent.*; +import java.util.stream.*; public class ProvidingBlockstore implements Blockstore { private final Blockstore target; + private final BlockMetadataStore metaDB; public final BlockingDeque toPublish = new LinkedBlockingDeque<>(); - public ProvidingBlockstore(Blockstore target) { + public ProvidingBlockstore(Blockstore target, BlockMetadataStore metaDB) { this.target = target; + this.metaDB = metaDB; } @Override @@ -45,7 +48,7 @@ public CompletableFuture rm(Cid c) { @Override public CompletableFuture> refs() { - return target.refs(); + return CompletableFuture.completedFuture(metaDB.list().collect(Collectors.toList())); } @Override @@ -55,6 +58,6 @@ public CompletableFuture bloomAdd(Cid cid) { @Override public CompletableFuture getBlockMetadata(Cid h) { - return target.getBlockMetadata(h); + return CompletableFuture.completedFuture(metaDB.get(h).get()); } } diff --git a/src/test/java/org/peergos/APIServiceTest.java b/src/test/java/org/peergos/APIServiceTest.java index 46a1dbe9..8143a9ed 100644 --- a/src/test/java/org/peergos/APIServiceTest.java +++ b/src/test/java/org/peergos/APIServiceTest.java @@ -44,7 +44,7 @@ public void runAPIServiceWithFileStorageTest() { @Test public void bulkGetTest() { - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, null, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore(), new RamBlockMetadataStore()), null, null, null, Optional.empty(), Collections.emptyList()); Cid cid1 = ipfs.blockstore.put("Hello".getBytes(), Cid.Codec.Raw).join(); Cid cid2= ipfs.blockstore.put("world!".getBytes(), Cid.Codec.Raw).join(); List wants = new ArrayList<>(); @@ -55,7 +55,7 @@ public void bulkGetTest() { } public static void runAPIServiceTest(Blockstore blocks) { - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, null, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks, new RamBlockMetadataStore()), null, null, null, Optional.empty(), Collections.emptyList()); Cid cid = Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"); Assert.assertFalse("cid found", ipfs.blockstore.has(cid).join()); String text = "Hello world!"; diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 7f628ae0..998d4889 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -64,6 +64,6 @@ public static EmbeddedIpfs build(List bootstrap, List CompletableFuture.completedFuture(true)); @@ -21,17 +24,17 @@ public void findOtherNode() throws Exception { node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); - HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); - Host node2 = builder2.build(); - node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node2); +// HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), +// new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); +// Host node2 = builder2.build(); +// node2.start().join(); +// IdentifyBuilder.addIdentifyProtocol(node2); try { // bootstrap node 2 - Kademlia dht2 = builder2.getWanDht().get(); - dht2.bootstrapRoutingTable(node2, BootstrapTest.BOOTSTRAP_NODES, addr -> !addr.contains("/wss/")); - dht2.bootstrap(node2); +// Kademlia dht2 = builder2.getWanDht().get(); +// dht2.bootstrapRoutingTable(node2, BootstrapTest.BOOTSTRAP_NODES, addr -> !addr.contains("/wss/")); +// dht2.bootstrap(node2); // bootstrap node 1 Kademlia dht1 = builder1.getWanDht().get(); @@ -39,7 +42,7 @@ public void findOtherNode() throws Exception { dht1.bootstrap(node1); // Check node1 can find node2 from kubo - Multihash peerId2 = Multihash.deserialize(node2.getPeerId().getBytes()); + Multihash peerId2 = Multihash.fromBase58("12D3KooWBN95Mu5roMQfLW8DDn3b5x2Z8Z92U1XVZRazew8j1CVp");//Multihash.deserialize(node2.getPeerId().getBytes()); List closestPeers = dht1.findClosestPeers(peerId2, 2, node1); Optional matching = closestPeers.stream() .filter(p -> p.peerId.equals(peerId2)) @@ -48,7 +51,7 @@ public void findOtherNode() throws Exception { throw new IllegalStateException("Couldn't find node2 from kubo!"); } finally { node1.stop(); - node2.stop(); +// node2.stop(); } } } diff --git a/src/test/java/org/peergos/P2pHttpChatTest.java b/src/test/java/org/peergos/P2pHttpChatTest.java index 0d28a408..b28f887c 100644 --- a/src/test/java/org/peergos/P2pHttpChatTest.java +++ b/src/test/java/org/peergos/P2pHttpChatTest.java @@ -9,14 +9,16 @@ import org.peergos.protocol.dht.*; import org.peergos.protocol.http.*; +import java.io.*; import java.nio.charset.*; import java.util.*; import java.util.concurrent.*; +import java.util.zip.*; public class P2pHttpChatTest { @Test - public void p2pHttpChat() { + public void p2pHttpChat() throws Exception { FullHttpResponse replyOk = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.buffer(0)); replyOk.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); HttpProtocol.Binding node1Http = new HttpProtocol.Binding((s, req, h) -> { @@ -28,10 +30,83 @@ public void p2pHttpChat() { new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(node1Http); Host node1 = builder1.build(); +// HttpProtocol.Binding node2Http = new HttpProtocol.Binding((s, req, h) -> { +// //System.out.println("Node 2 received: " + req); +// //printBody(req); +// h.accept(replyOk.retain()); +// }); +// HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), +// new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) +// .addProtocol(node2Http); +// Host node2 = builder2.build(); + node1.start().join(); +// node2.start().join(); + + try { +// Multiaddr address1 = node1.listenAddresses().get(0); + Multiaddr address2 = new Multiaddr("/ip4/50.116.48.246/tcp/4001/p2p/QmUUv85Z8fq5VMBDVRZfSVVrNKss5J5M2j17mB3CWVxK78");//node2.listenAddresses().get(0); +// Multiaddr address2 = new Multiaddr("/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWBN95Mu5roMQfLW8DDn3b5x2Z8Z92U1XVZRazew8j1CVp"); + + int count = 200; + long totalDuration = 0; + for (int i = 0; i < count; i++) { +// byte[] msg1 = "G'day from node1!".getBytes(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/peergos/v0/core/getUsernamesGzip"); +// httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg1.length); + HttpProtocol.HttpController proxier1 = node1Http.dial(node1, address2) + .getController().join(); + long t1 = System.currentTimeMillis(); + FullHttpResponse resp = proxier1.send(httpRequest.retain()).join(); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + resp.content().readBytes(bout, resp.content().readableBytes()); + resp.release(); + String body = new String(new GZIPInputStream(new ByteArrayInputStream(bout.toByteArray())).readAllBytes()); + long t2 = System.currentTimeMillis(); + //System.out.println("P2P HTTP request took " + (t2 - t1) + "ms"); + totalDuration += t2 - t1; + +// byte[] msg2 = "G'day node1! I'm node2.".getBytes(); +// FullHttpRequest httpRequest2 = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg2)); +// httpRequest2.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg2.length); +// HttpProtocol.HttpController proxier2 = node2Http.dial(node2, address1) +// .getController().join(); +// long t3 = System.currentTimeMillis(); +// proxier2.send(httpRequest2.retain()).join().release(); +// long t4 = System.currentTimeMillis(); +// //System.out.println("P2P HTTP request took " + (t4 - t3) + "ms"); +// totalDuration += t4 - t3; + } + System.out.println("Average: " + (totalDuration / (count * 2))); + } finally { + node1.stop(); +// node2.stop(); + } + } + public static void printBody(HttpRequest req) { + if (req instanceof FullHttpRequest) { + ByteBuf content = ((FullHttpRequest) req).content(); + System.out.println(content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset())); + } + + } + + @Test + public void p2pHttpErrors() throws Exception { + FullHttpResponse replyError = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.buffer(0)); + replyError.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); + HttpProtocol.Binding node1Http = new HttpProtocol.Binding((s, req, h) -> { + //System.out.println("Node 1 received: " + req); + //printBody(req); + h.accept(replyError.retain()); + }); + HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), + new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + .addProtocol(node1Http); + Host node1 = builder1.build(); HttpProtocol.Binding node2Http = new HttpProtocol.Binding((s, req, h) -> { //System.out.println("Node 2 received: " + req); //printBody(req); - h.accept(replyOk.retain()); + h.accept(replyError.retain()); }); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) @@ -41,33 +116,24 @@ public void p2pHttpChat() { node2.start().join(); try { - Multiaddr address1 = node1.listenAddresses().get(0); Multiaddr address2 = node2.listenAddresses().get(0); int count = 200; long totalDuration = 0; for (int i = 0; i < count; i++) { - byte[] msg1 = "G'day from node1!".getBytes(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg1)); - httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg1.length); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/peergos/v0/core/getUsernamesGzip"); +// httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg1.length); HttpProtocol.HttpController proxier1 = node1Http.dial(node1, address2) .getController().join(); long t1 = System.currentTimeMillis(); - proxier1.send(httpRequest.retain()).join().release(); + FullHttpResponse resp = proxier1.send(httpRequest.retain()).join(); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + resp.content().readBytes(bout, resp.content().readableBytes()); + resp.release(); + String body = new String(new ByteArrayInputStream(bout.toByteArray()).readAllBytes()); long t2 = System.currentTimeMillis(); //System.out.println("P2P HTTP request took " + (t2 - t1) + "ms"); totalDuration += t2 - t1; - - byte[] msg2 = "G'day node1! I'm node2.".getBytes(); - FullHttpRequest httpRequest2 = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg2)); - httpRequest2.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg2.length); - HttpProtocol.HttpController proxier2 = node2Http.dial(node2, address1) - .getController().join(); - long t3 = System.currentTimeMillis(); - proxier2.send(httpRequest2.retain()).join().release(); - long t4 = System.currentTimeMillis(); - //System.out.println("P2P HTTP request took " + (t4 - t3) + "ms"); - totalDuration += t4 - t3; } System.out.println("Average: " + (totalDuration / (count * 2))); } finally { @@ -75,11 +141,4 @@ public void p2pHttpChat() { node2.stop(); } } - public static void printBody(HttpRequest req) { - if (req instanceof FullHttpRequest) { - ByteBuf content = ((FullHttpRequest) req).content(); - System.out.println(content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset())); - } - - } } diff --git a/src/test/java/org/peergos/P2pHttpTest.java b/src/test/java/org/peergos/P2pHttpTest.java index 2e2e1956..59cb5506 100644 --- a/src/test/java/org/peergos/P2pHttpTest.java +++ b/src/test/java/org/peergos/P2pHttpTest.java @@ -100,7 +100,7 @@ public void p2pTest() { server2.createContext("/", httpExchange -> { try { byte[] body = responseText.getBytes(); - httpExchange.sendResponseHeaders(200, body.length); + httpExchange.sendResponseHeaders(400, body.length); httpExchange.getResponseBody().write(body); } catch (Exception e) { e.printStackTrace(); @@ -114,8 +114,14 @@ public void p2pTest() { URL target = new URL("http", "localhost", port, "/p2p/" + peerAsCid + "/http/message" + urlParam); - Response reply = send(target, "POST", requestBody.getBytes(), requestHeaders); - Assert.assertTrue("reply", responseText.equals(new String(reply.body))); + for (int i=0; i<100; i++) { + try { + Response reply = send(target, "POST", requestBody.getBytes(), requestHeaders); + Assert.assertTrue(reply.statusCode == 400); + } catch (IOException e) { + System.out.println(); + } + } } catch (IOException ioe) { ioe.printStackTrace(); Assert.assertTrue("IOException", false); From a393b79eba2aabf42e149e05b5d0c8f198374372 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 10 Oct 2023 21:58:38 +0100 Subject: [PATCH 044/218] revert test changes --- .../java/org/peergos/P2pHttpChatTest.java | 109 ++++-------------- src/test/java/org/peergos/P2pHttpTest.java | 12 +- 2 files changed, 28 insertions(+), 93 deletions(-) diff --git a/src/test/java/org/peergos/P2pHttpChatTest.java b/src/test/java/org/peergos/P2pHttpChatTest.java index b28f887c..0d28a408 100644 --- a/src/test/java/org/peergos/P2pHttpChatTest.java +++ b/src/test/java/org/peergos/P2pHttpChatTest.java @@ -9,16 +9,14 @@ import org.peergos.protocol.dht.*; import org.peergos.protocol.http.*; -import java.io.*; import java.nio.charset.*; import java.util.*; import java.util.concurrent.*; -import java.util.zip.*; public class P2pHttpChatTest { @Test - public void p2pHttpChat() throws Exception { + public void p2pHttpChat() { FullHttpResponse replyOk = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.buffer(0)); replyOk.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); HttpProtocol.Binding node1Http = new HttpProtocol.Binding((s, req, h) -> { @@ -30,83 +28,10 @@ public void p2pHttpChat() throws Exception { new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(node1Http); Host node1 = builder1.build(); -// HttpProtocol.Binding node2Http = new HttpProtocol.Binding((s, req, h) -> { -// //System.out.println("Node 2 received: " + req); -// //printBody(req); -// h.accept(replyOk.retain()); -// }); -// HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), -// new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) -// .addProtocol(node2Http); -// Host node2 = builder2.build(); - node1.start().join(); -// node2.start().join(); - - try { -// Multiaddr address1 = node1.listenAddresses().get(0); - Multiaddr address2 = new Multiaddr("/ip4/50.116.48.246/tcp/4001/p2p/QmUUv85Z8fq5VMBDVRZfSVVrNKss5J5M2j17mB3CWVxK78");//node2.listenAddresses().get(0); -// Multiaddr address2 = new Multiaddr("/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWBN95Mu5roMQfLW8DDn3b5x2Z8Z92U1XVZRazew8j1CVp"); - - int count = 200; - long totalDuration = 0; - for (int i = 0; i < count; i++) { -// byte[] msg1 = "G'day from node1!".getBytes(); - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/peergos/v0/core/getUsernamesGzip"); -// httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg1.length); - HttpProtocol.HttpController proxier1 = node1Http.dial(node1, address2) - .getController().join(); - long t1 = System.currentTimeMillis(); - FullHttpResponse resp = proxier1.send(httpRequest.retain()).join(); - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - resp.content().readBytes(bout, resp.content().readableBytes()); - resp.release(); - String body = new String(new GZIPInputStream(new ByteArrayInputStream(bout.toByteArray())).readAllBytes()); - long t2 = System.currentTimeMillis(); - //System.out.println("P2P HTTP request took " + (t2 - t1) + "ms"); - totalDuration += t2 - t1; - -// byte[] msg2 = "G'day node1! I'm node2.".getBytes(); -// FullHttpRequest httpRequest2 = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg2)); -// httpRequest2.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg2.length); -// HttpProtocol.HttpController proxier2 = node2Http.dial(node2, address1) -// .getController().join(); -// long t3 = System.currentTimeMillis(); -// proxier2.send(httpRequest2.retain()).join().release(); -// long t4 = System.currentTimeMillis(); -// //System.out.println("P2P HTTP request took " + (t4 - t3) + "ms"); -// totalDuration += t4 - t3; - } - System.out.println("Average: " + (totalDuration / (count * 2))); - } finally { - node1.stop(); -// node2.stop(); - } - } - public static void printBody(HttpRequest req) { - if (req instanceof FullHttpRequest) { - ByteBuf content = ((FullHttpRequest) req).content(); - System.out.println(content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset())); - } - - } - - @Test - public void p2pHttpErrors() throws Exception { - FullHttpResponse replyError = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.buffer(0)); - replyError.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); - HttpProtocol.Binding node1Http = new HttpProtocol.Binding((s, req, h) -> { - //System.out.println("Node 1 received: " + req); - //printBody(req); - h.accept(replyError.retain()); - }); - HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) - .addProtocol(node1Http); - Host node1 = builder1.build(); HttpProtocol.Binding node2Http = new HttpProtocol.Binding((s, req, h) -> { //System.out.println("Node 2 received: " + req); //printBody(req); - h.accept(replyError.retain()); + h.accept(replyOk.retain()); }); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) @@ -116,24 +41,33 @@ public void p2pHttpErrors() throws Exception { node2.start().join(); try { + Multiaddr address1 = node1.listenAddresses().get(0); Multiaddr address2 = node2.listenAddresses().get(0); int count = 200; long totalDuration = 0; for (int i = 0; i < count; i++) { - FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/peergos/v0/core/getUsernamesGzip"); -// httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg1.length); + byte[] msg1 = "G'day from node1!".getBytes(); + FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg1)); + httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg1.length); HttpProtocol.HttpController proxier1 = node1Http.dial(node1, address2) .getController().join(); long t1 = System.currentTimeMillis(); - FullHttpResponse resp = proxier1.send(httpRequest.retain()).join(); - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - resp.content().readBytes(bout, resp.content().readableBytes()); - resp.release(); - String body = new String(new ByteArrayInputStream(bout.toByteArray()).readAllBytes()); + proxier1.send(httpRequest.retain()).join().release(); long t2 = System.currentTimeMillis(); //System.out.println("P2P HTTP request took " + (t2 - t1) + "ms"); totalDuration += t2 - t1; + + byte[] msg2 = "G'day node1! I'm node2.".getBytes(); + FullHttpRequest httpRequest2 = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", Unpooled.copiedBuffer(msg2)); + httpRequest2.headers().set(HttpHeaderNames.CONTENT_LENGTH, msg2.length); + HttpProtocol.HttpController proxier2 = node2Http.dial(node2, address1) + .getController().join(); + long t3 = System.currentTimeMillis(); + proxier2.send(httpRequest2.retain()).join().release(); + long t4 = System.currentTimeMillis(); + //System.out.println("P2P HTTP request took " + (t4 - t3) + "ms"); + totalDuration += t4 - t3; } System.out.println("Average: " + (totalDuration / (count * 2))); } finally { @@ -141,4 +75,11 @@ public void p2pHttpErrors() throws Exception { node2.stop(); } } + public static void printBody(HttpRequest req) { + if (req instanceof FullHttpRequest) { + ByteBuf content = ((FullHttpRequest) req).content(); + System.out.println(content.getCharSequence(0, content.readableBytes(), Charset.defaultCharset())); + } + + } } diff --git a/src/test/java/org/peergos/P2pHttpTest.java b/src/test/java/org/peergos/P2pHttpTest.java index 59cb5506..2e2e1956 100644 --- a/src/test/java/org/peergos/P2pHttpTest.java +++ b/src/test/java/org/peergos/P2pHttpTest.java @@ -100,7 +100,7 @@ public void p2pTest() { server2.createContext("/", httpExchange -> { try { byte[] body = responseText.getBytes(); - httpExchange.sendResponseHeaders(400, body.length); + httpExchange.sendResponseHeaders(200, body.length); httpExchange.getResponseBody().write(body); } catch (Exception e) { e.printStackTrace(); @@ -114,14 +114,8 @@ public void p2pTest() { URL target = new URL("http", "localhost", port, "/p2p/" + peerAsCid + "/http/message" + urlParam); - for (int i=0; i<100; i++) { - try { - Response reply = send(target, "POST", requestBody.getBytes(), requestHeaders); - Assert.assertTrue(reply.statusCode == 400); - } catch (IOException e) { - System.out.println(); - } - } + Response reply = send(target, "POST", requestBody.getBytes(), requestHeaders); + Assert.assertTrue("reply", responseText.equals(new String(reply.body))); } catch (IOException ioe) { ioe.printStackTrace(); Assert.assertTrue("IOException", false); From 5294258656b429dddae4418ba9492c9f889a6f08 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 10 Oct 2023 22:12:05 +0100 Subject: [PATCH 045/218] Make {S3, CachingBlockMetadataStore}.refs only use metadb Revert addition of metadb to ProvidingBlockstore --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 +- .../java/org/peergos/blockstore/ProvidingBlockstore.java | 8 +++----- .../blockstore/metadatadb/CachingBlockMetadataStore.java | 4 ++-- src/main/java/org/peergos/blockstore/s3/S3Blockstore.java | 6 +++++- src/test/java/org/peergos/APIServiceTest.java | 4 ++-- src/test/java/org/peergos/HandlerTest.java | 6 +++--- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 78bce4ef..78a178a4 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -171,7 +171,7 @@ public static EmbeddedIpfs build(RecordStore records, IdentitySection identity, BlockRequestAuthoriser authoriser, Optional handler) { - ProvidingBlockstore blockstore = new ProvidingBlockstore(blocks,metaDB); + ProvidingBlockstore blockstore = new ProvidingBlockstore(blocks); ProviderStore providers = new RamProviderStore(); HostBuilder builder = new HostBuilder().setIdentity(identity.privKeyProtobuf).listen(swarmAddresses); diff --git a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java index e9304879..ffd0acb1 100644 --- a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java +++ b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java @@ -11,12 +11,10 @@ public class ProvidingBlockstore implements Blockstore { private final Blockstore target; - private final BlockMetadataStore metaDB; public final BlockingDeque toPublish = new LinkedBlockingDeque<>(); - public ProvidingBlockstore(Blockstore target, BlockMetadataStore metaDB) { + public ProvidingBlockstore(Blockstore target) { this.target = target; - this.metaDB = metaDB; } @Override @@ -48,7 +46,7 @@ public CompletableFuture rm(Cid c) { @Override public CompletableFuture> refs() { - return CompletableFuture.completedFuture(metaDB.list().collect(Collectors.toList())); + return target.refs(); } @Override @@ -58,6 +56,6 @@ public CompletableFuture bloomAdd(Cid cid) { @Override public CompletableFuture getBlockMetadata(Cid h) { - return CompletableFuture.completedFuture(metaDB.get(h).get()); + return target.getBlockMetadata(h); } } diff --git a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java index 4ea2f218..2e223877 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; +import java.util.stream.*; public class CachingBlockMetadataStore implements Blockstore { @@ -84,7 +84,7 @@ public CompletableFuture bloomAdd(Cid cid) { @Override public CompletableFuture> refs() { - return target.refs(); + return CompletableFuture.completedFuture(metadata.list().collect(Collectors.toList())); } @Override diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index 0280b6e9..dfd3b968 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -179,7 +179,7 @@ public void updateMetadataStoreIfEmpty() { if (blockMetadata.size() > 0) return; LOG.info("Updating block metadata store from S3. Listing blocks..."); - List all = refs().join(); + List all = directRefs().join(); LOG.info("Updating block metadata store from S3. Updating db with " + all.size() + " blocks..."); int updateParallelism = 10; @@ -344,6 +344,10 @@ public CompletableFuture bloomAdd(Cid cid) { @Override public CompletableFuture> refs() { + return CompletableFuture.completedFuture(blockMetadata.list().collect(Collectors.toList())); + } + + public CompletableFuture> directRefs() { List cidList = new ArrayList<>(); applyToAll(obj -> cidList.add(keyToHash(obj.key)), Integer.MAX_VALUE); return CompletableFuture.completedFuture(cidList); diff --git a/src/test/java/org/peergos/APIServiceTest.java b/src/test/java/org/peergos/APIServiceTest.java index 8143a9ed..46a1dbe9 100644 --- a/src/test/java/org/peergos/APIServiceTest.java +++ b/src/test/java/org/peergos/APIServiceTest.java @@ -44,7 +44,7 @@ public void runAPIServiceWithFileStorageTest() { @Test public void bulkGetTest() { - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore(), new RamBlockMetadataStore()), null, null, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, null, null, Optional.empty(), Collections.emptyList()); Cid cid1 = ipfs.blockstore.put("Hello".getBytes(), Cid.Codec.Raw).join(); Cid cid2= ipfs.blockstore.put("world!".getBytes(), Cid.Codec.Raw).join(); List wants = new ArrayList<>(); @@ -55,7 +55,7 @@ public void bulkGetTest() { } public static void runAPIServiceTest(Blockstore blocks) { - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks, new RamBlockMetadataStore()), null, null, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, null, null, Optional.empty(), Collections.emptyList()); Cid cid = Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"); Assert.assertFalse("cid found", ipfs.blockstore.has(cid).join()); String text = "Hello world!"; diff --git a/src/test/java/org/peergos/HandlerTest.java b/src/test/java/org/peergos/HandlerTest.java index 1d073b8c..e7589c93 100644 --- a/src/test/java/org/peergos/HandlerTest.java +++ b/src/test/java/org/peergos/HandlerTest.java @@ -44,7 +44,7 @@ public void codecTest() { apiServer = HttpServer.create(localAPIAddress, 500); Blockstore blocks = new TypeLimitedBlockstore(new RamBlockstore(), Set.of(Cid.Codec.Raw)); - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks, new RamBlockMetadataStore()), null, null, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, null, null, Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); @@ -97,7 +97,7 @@ public void findBlockProviderTest() throws IOException { InetSocketAddress localAPIAddress = new InetSocketAddress(apiAddress.getHost(), apiAddress.getPort()); apiServer = HttpServer.create(localAPIAddress, 500); - EmbeddedIpfs ipfs = new EmbeddedIpfs(node1, new ProvidingBlockstore(new RamBlockstore(), new RamBlockMetadataStore()), null, dht, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(node1, new ProvidingBlockstore(new RamBlockstore()), null, dht, null, Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); @@ -122,7 +122,7 @@ public void blockMethodsTest() { InetSocketAddress localAPIAddress = new InetSocketAddress(apiAddress.getHost(), apiAddress.getPort()); apiServer = HttpServer.create(localAPIAddress, 500); - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore(), new RamBlockMetadataStore()), null, null, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, null, null, Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); From 0f7a36c114154d0b9f3caf285da9db1392877cde Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 10 Oct 2023 22:39:49 +0100 Subject: [PATCH 046/218] Remove unneeded metadb arg to EmbeddedIpfs.build() --- src/main/java/org/peergos/EmbeddedIpfs.java | 1 - src/main/java/org/peergos/Nabu.java | 1 - src/test/java/org/peergos/EmbeddedIpfsTest.java | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 78a178a4..f974d632 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -165,7 +165,6 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMeta public static EmbeddedIpfs build(RecordStore records, Blockstore blocks, - BlockMetadataStore metaDB, List swarmAddresses, List bootstrap, IdentitySection identity, diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 1c31166e..1d67ae6c 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -54,7 +54,6 @@ public Nabu(Args args) throws Exception { BlockMetadataStore meta = buildBlockMetadata(args); EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, buildBlockStore(config, ipfsPath, meta), - meta, config.addresses.getSwarmAddresses(), config.bootstrap.getBootstrapAddresses(), config.identity, diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 998d4889..7f628ae0 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -64,6 +64,6 @@ public static EmbeddedIpfs build(List bootstrap, List Date: Tue, 10 Oct 2023 23:04:36 +0100 Subject: [PATCH 047/218] Log duration of block provider runs --- src/main/java/org/peergos/PeriodicBlockProvider.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/peergos/PeriodicBlockProvider.java b/src/main/java/org/peergos/PeriodicBlockProvider.java index 5ee72a73..4332ec38 100644 --- a/src/main/java/org/peergos/PeriodicBlockProvider.java +++ b/src/main/java/org/peergos/PeriodicBlockProvider.java @@ -48,7 +48,11 @@ public void stop() { public void run() { while (running.get()) { try { + LOG.info("Starting block provider run"); + long t0 = System.currentTimeMillis(); publish(getBlocks.get()); + long t1 = System.currentTimeMillis(); + LOG.info("Finished block provider run in " + (t1 - t0)/1_000 + "s"); Thread.sleep(reprovideIntervalMillis); } catch (Throwable e) { LOG.log(Level.WARNING, e.getMessage(), e); From 6907cf65c8e179735a9667e53ca7199939facff9 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 11 Oct 2023 09:56:22 +0100 Subject: [PATCH 048/218] revert test breakage --- src/test/java/org/peergos/KademliaTest.java | 22 ++++++++++----------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 010708d6..059a4d3f 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -15,8 +15,6 @@ public class KademliaTest { @Test public void findOtherNode() throws Exception { - Cid hash = Cid.decode("bafzaajaiaejcafx7o77di6q5lezyzopbxjoqgujrrfou4zijghi5jonchk7zchat"); - Multihash d = hash.bareMultihash(); RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); @@ -24,17 +22,17 @@ public void findOtherNode() throws Exception { node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); -// HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), -// new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); -// Host node2 = builder2.build(); -// node2.start().join(); -// IdentifyBuilder.addIdentifyProtocol(node2); + HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), + new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + Host node2 = builder2.build(); + node2.start().join(); + IdentifyBuilder.addIdentifyProtocol(node2); try { // bootstrap node 2 -// Kademlia dht2 = builder2.getWanDht().get(); -// dht2.bootstrapRoutingTable(node2, BootstrapTest.BOOTSTRAP_NODES, addr -> !addr.contains("/wss/")); -// dht2.bootstrap(node2); + Kademlia dht2 = builder2.getWanDht().get(); + dht2.bootstrapRoutingTable(node2, BootstrapTest.BOOTSTRAP_NODES, addr -> !addr.contains("/wss/")); + dht2.bootstrap(node2); // bootstrap node 1 Kademlia dht1 = builder1.getWanDht().get(); @@ -42,7 +40,7 @@ public void findOtherNode() throws Exception { dht1.bootstrap(node1); // Check node1 can find node2 from kubo - Multihash peerId2 = Multihash.fromBase58("12D3KooWBN95Mu5roMQfLW8DDn3b5x2Z8Z92U1XVZRazew8j1CVp");//Multihash.deserialize(node2.getPeerId().getBytes()); + Multihash peerId2 = Multihash.deserialize(node2.getPeerId().getBytes()); List closestPeers = dht1.findClosestPeers(peerId2, 2, node1); Optional matching = closestPeers.stream() .filter(p -> p.peerId.equals(peerId2)) @@ -51,7 +49,7 @@ public void findOtherNode() throws Exception { throw new IllegalStateException("Couldn't find node2 from kubo!"); } finally { node1.stop(); -// node2.stop(); + node2.stop(); } } } From f61bc2cbf2b2437fa3b06101bcc95a1ffbf411a9 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 11 Oct 2023 10:11:11 +0100 Subject: [PATCH 049/218] Make S3 gets check metadata store first --- src/main/java/org/peergos/blockstore/s3/S3Blockstore.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index dfd3b968..108a45fa 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -265,6 +265,8 @@ private CompletableFuture> getSizeWithoutRetry(Cid cid) { @Override public CompletableFuture> get(Cid cid) { + if (blockMetadata.get(cid).isEmpty()) + return CompletableFuture.completedFuture(Optional.empty()); return getWithBackoff(() -> getWithoutRetry(cid)); } From a2fa00d5b4c78c8afda2ba6b4d8c966b4224a50c Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 11 Oct 2023 12:58:49 +0100 Subject: [PATCH 050/218] Bound address book size --- src/main/java/org/peergos/RamAddressBook.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/RamAddressBook.java b/src/main/java/org/peergos/RamAddressBook.java index 59c24ca0..9ef35429 100644 --- a/src/main/java/org/peergos/RamAddressBook.java +++ b/src/main/java/org/peergos/RamAddressBook.java @@ -9,7 +9,11 @@ public class RamAddressBook implements AddressBook { - Map> addresses = new ConcurrentHashMap<>(); + private final Map> addresses; + + public RamAddressBook() { + addresses = Collections.synchronizedMap(new LRUCache<>(10_000)); + } @NotNull @Override @@ -36,4 +40,17 @@ public CompletableFuture setAddrs(@NotNull PeerId peerId, long ttl, @NotNu addresses.put(peerId, val); return CompletableFuture.completedFuture(null); } + + public static class LRUCache extends LinkedHashMap { + private final int cacheSize; + + public LRUCache(int cacheSize) { + super(16, 0.75f, true); + this.cacheSize = cacheSize; + } + + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() >= cacheSize; + } + } } From ed40b607f554ff1f3ab69f6e215227a4ba0d54c4 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 11 Oct 2023 13:34:13 +0100 Subject: [PATCH 051/218] Reduce provider logspam from failed dials --- src/main/java/org/peergos/PeriodicBlockProvider.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/PeriodicBlockProvider.java b/src/main/java/org/peergos/PeriodicBlockProvider.java index 4332ec38..d15c2f2a 100644 --- a/src/main/java/org/peergos/PeriodicBlockProvider.java +++ b/src/main/java/org/peergos/PeriodicBlockProvider.java @@ -77,10 +77,19 @@ public void publish(Stream blocks) { PeerAddresses ourAddrs = PeerAddresses.fromHost(us); List> published = blocks.parallel() - .map(ref -> dht.provideBlock(ref, us, ourAddrs)) + .map(ref -> publish(ref, ourAddrs)) .collect(Collectors.toList()); for (CompletableFuture fut : published) { fut.join(); } } + + public CompletableFuture publish(Cid h, PeerAddresses ourAddrs) { + try { + return dht.provideBlock(h, us, ourAddrs); + } catch (Exception e) { + LOG.fine("Couldn't provide block: " + e.getMessage()); + return CompletableFuture.completedFuture(null); + } + } } From 5405aff7c6ee8a9602b183e4bdaae61be32a032a Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 11 Oct 2023 14:11:04 +0100 Subject: [PATCH 052/218] Reduce provider logspam from failed dials --- src/main/java/org/peergos/PeriodicBlockProvider.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/PeriodicBlockProvider.java b/src/main/java/org/peergos/PeriodicBlockProvider.java index d15c2f2a..15eac273 100644 --- a/src/main/java/org/peergos/PeriodicBlockProvider.java +++ b/src/main/java/org/peergos/PeriodicBlockProvider.java @@ -86,8 +86,11 @@ public void publish(Stream blocks) { public CompletableFuture publish(Cid h, PeerAddresses ourAddrs) { try { - return dht.provideBlock(h, us, ourAddrs); - } catch (Exception e) { + return dht.provideBlock(h, us, ourAddrs).exceptionally(t -> { + LOG.fine("Couldn't provide block: " + t.getMessage()); + return null; + }); + } catch (Throwable e) { LOG.fine("Couldn't provide block: " + e.getMessage()); return CompletableFuture.completedFuture(null); } From 04600d1cc83b3dc2f2a6881ac50946e7d9ba2de4 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 11 Oct 2023 17:24:47 +0100 Subject: [PATCH 053/218] Make providing blocks optional --- src/main/java/org/peergos/EmbeddedIpfs.java | 30 +++++++++++-------- src/main/java/org/peergos/Nabu.java | 1 + src/test/java/org/peergos/APIServiceTest.java | 6 ++-- .../java/org/peergos/EmbeddedIpfsTest.java | 2 +- src/test/java/org/peergos/HandlerTest.java | 8 +++-- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index f974d632..1f9ba423 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -1,5 +1,6 @@ package org.peergos; +import io.ipfs.cid.*; import io.ipfs.multiaddr.*; import io.ipfs.multihash.Multihash; import io.libp2p.core.*; @@ -36,7 +37,7 @@ public class EmbeddedIpfs { private static final Logger LOG = Logging.LOG(); public final Host node; - public final ProvidingBlockstore blockstore; + public final Blockstore blockstore; public final BlockService blocks; public final RecordStore records; @@ -44,15 +45,16 @@ public class EmbeddedIpfs { public final Bitswap bitswap; public final Optional p2pHttp; private final List bootstrap; - private final PeriodicBlockProvider blockProvider; + private final Optional blockProvider; public EmbeddedIpfs(Host node, - ProvidingBlockstore blockstore, + Blockstore blockstore, RecordStore records, Kademlia dht, Bitswap bitswap, Optional p2pHttp, - List bootstrap) { + List bootstrap, + Optional> newBlockProvider) { this.node = node; this.blockstore = blockstore; this.records = records; @@ -61,8 +63,8 @@ public EmbeddedIpfs(Host node, this.p2pHttp = p2pHttp; this.bootstrap = bootstrap; this.blocks = new BitswapBlockService(node, bitswap, dht); - this.blockProvider = new PeriodicBlockProvider(22 * 3600_000L, - () -> blockstore.refs().join().stream(), node, dht, blockstore.toPublish); + this.blockProvider = newBlockProvider.map(q -> new PeriodicBlockProvider(22 * 3600_000L, + () -> blockstore.refs().join().stream(), node, dht, q)); } public List getBlocks(List wants, Set peers, boolean addToLocal) { @@ -108,16 +110,14 @@ public void start() { dht.bootstrap(node); dht.startBootstrapThread(node); - blockProvider.start(); + blockProvider.ifPresent(p -> p.start()); } public CompletableFuture stop() throws Exception { if (records != null) { records.close(); } - if (blockProvider != null) { - blockProvider.stop(); - } + blockProvider.ifPresent(b -> b.stop()); return node != null ? node.stop() : CompletableFuture.completedFuture(null); } @@ -165,12 +165,15 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMeta public static EmbeddedIpfs build(RecordStore records, Blockstore blocks, + boolean provideBlocks, List swarmAddresses, List bootstrap, IdentitySection identity, BlockRequestAuthoriser authoriser, Optional handler) { - ProvidingBlockstore blockstore = new ProvidingBlockstore(blocks); + Blockstore blockstore = provideBlocks ? + new ProvidingBlockstore(blocks) : + blocks; ProviderStore providers = new RamProviderStore(); HostBuilder builder = new HostBuilder().setIdentity(identity.privKeyProtobuf).listen(swarmAddresses); @@ -195,7 +198,10 @@ public static EmbeddedIpfs build(RecordStore records, Host node = builder.addProtocols(protocols).build(); - return new EmbeddedIpfs(node, blockstore, records, dht, bitswap, httpHandler, bootstrap); + Optional> newBlockProvider = provideBlocks ? + Optional.of(((ProvidingBlockstore)blockstore).toPublish) : + Optional.empty(); + return new EmbeddedIpfs(node, blockstore, records, dht, bitswap, httpHandler, bootstrap, newBlockProvider); } public static Multiaddr[] getAddresses(Host node, Kademlia dht, Multihash targetNodeId) throws ConnectionException { diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 1d67ae6c..0d4fca40 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -54,6 +54,7 @@ public Nabu(Args args) throws Exception { BlockMetadataStore meta = buildBlockMetadata(args); EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, buildBlockStore(config, ipfsPath, meta), + true, config.addresses.getSwarmAddresses(), config.bootstrap.getBootstrapAddresses(), config.identity, diff --git a/src/test/java/org/peergos/APIServiceTest.java b/src/test/java/org/peergos/APIServiceTest.java index 46a1dbe9..e8433115 100644 --- a/src/test/java/org/peergos/APIServiceTest.java +++ b/src/test/java/org/peergos/APIServiceTest.java @@ -44,7 +44,8 @@ public void runAPIServiceWithFileStorageTest() { @Test public void bulkGetTest() { - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, null, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, + null, null, Optional.empty(), Collections.emptyList(), Optional.empty()); Cid cid1 = ipfs.blockstore.put("Hello".getBytes(), Cid.Codec.Raw).join(); Cid cid2= ipfs.blockstore.put("world!".getBytes(), Cid.Codec.Raw).join(); List wants = new ArrayList<>(); @@ -55,7 +56,8 @@ public void bulkGetTest() { } public static void runAPIServiceTest(Blockstore blocks) { - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, null, null, Optional.empty(), Collections.emptyList()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, + null, null, Optional.empty(), Collections.emptyList(), Optional.empty()); Cid cid = Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"); Assert.assertFalse("cid found", ipfs.blockstore.has(cid).join()); String text = "Hello world!"; diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 7f628ae0..504bb2af 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -64,6 +64,6 @@ public static EmbeddedIpfs build(List bootstrap, List Date: Thu, 12 Oct 2023 13:52:28 +0100 Subject: [PATCH 054/218] Put the filtered blockstore on the outside --- src/main/java/org/peergos/EmbeddedIpfs.java | 26 +++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 1f9ba423..4a43ba3d 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -136,7 +136,7 @@ public static BlockMetadataStore buildBlockMetadata(Args a) { } } public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMetadataStore meta) { - Blockstore blocks = null; + Blockstore blocks; if (config.datastore.blockMount.prefix.equals("flatfs.datastore")) { blocks = new FileBlockstore(ipfsPath); }else if (config.datastore.blockMount.prefix.equals("s3.datastore")) { @@ -146,21 +146,27 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMeta } else { throw new IllegalStateException("Unrecognized datastore prefix: " + config.datastore.blockMount.prefix); } - Blockstore blockStore; + Blockstore withMetadb = config.datastore.blockMount.prefix.equals("s3.datastore") ? + blocks : + new CachingBlockMetadataStore(blocks, meta); + + Blockstore typeLimited = config.datastore.allowedCodecs.codecs.isEmpty() ? + withMetadb : + new TypeLimitedBlockstore(withMetadb, config.datastore.allowedCodecs.codecs); + + return filteredBlockStore(typeLimited, config); + } + + public static Blockstore filteredBlockStore(Blockstore blocks, Config config) { if (config.datastore.filter.type == FilterType.BLOOM) { - blockStore = FilteredBlockstore.bloomBased(blocks, config.datastore.filter.falsePositiveRate); + return FilteredBlockstore.bloomBased(blocks, config.datastore.filter.falsePositiveRate); } else if(config.datastore.filter.type == FilterType.INFINI) { - blockStore = FilteredBlockstore.infiniBased(blocks, config.datastore.filter.falsePositiveRate); + return FilteredBlockstore.infiniBased(blocks, config.datastore.filter.falsePositiveRate); } else if(config.datastore.filter.type == FilterType.NONE) { - blockStore = blocks; + return blocks; } else { throw new IllegalStateException("Unhandled filter type: " + config.datastore.filter.type); } - Blockstore filteredBlockStore = config.datastore.allowedCodecs.codecs.isEmpty() ? - blockStore : new TypeLimitedBlockstore(blockStore, config.datastore.allowedCodecs.codecs); - - return config.datastore.blockMount.prefix.equals("s3.datastore") ? filteredBlockStore - : new CachingBlockMetadataStore(filteredBlockStore, meta); } public static EmbeddedIpfs build(RecordStore records, From 94515fc33328a4cbb10091a3bea6a1f0d3722826 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 12 Oct 2023 13:54:54 +0100 Subject: [PATCH 055/218] Actually we want type limited on the outside --- src/main/java/org/peergos/EmbeddedIpfs.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 4a43ba3d..3a356f30 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -139,7 +139,7 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMeta Blockstore blocks; if (config.datastore.blockMount.prefix.equals("flatfs.datastore")) { blocks = new FileBlockstore(ipfsPath); - }else if (config.datastore.blockMount.prefix.equals("s3.datastore")) { + } else if (config.datastore.blockMount.prefix.equals("s3.datastore")) { S3Blockstore s3blocks = new S3Blockstore(config.datastore.blockMount.getParams(), meta); blocks = s3blocks; s3blocks.updateMetadataStoreIfEmpty(); @@ -150,11 +150,13 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMeta blocks : new CachingBlockMetadataStore(blocks, meta); - Blockstore typeLimited = config.datastore.allowedCodecs.codecs.isEmpty() ? - withMetadb : - new TypeLimitedBlockstore(withMetadb, config.datastore.allowedCodecs.codecs); + return typeLimited(filteredBlockStore(withMetadb, config), config); + } - return filteredBlockStore(typeLimited, config); + public static Blockstore typeLimited(Blockstore blocks, Config config) { + return config.datastore.allowedCodecs.codecs.isEmpty() ? + blocks : + new TypeLimitedBlockstore(blocks, config.datastore.allowedCodecs.codecs) } public static Blockstore filteredBlockStore(Blockstore blocks, Config config) { From e289547d3ec04d5a28edc45b5afd656de0654d4f Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 12 Oct 2023 13:58:17 +0100 Subject: [PATCH 056/218] typo --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 3a356f30..40ae7bd3 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -156,7 +156,7 @@ public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMeta public static Blockstore typeLimited(Blockstore blocks, Config config) { return config.datastore.allowedCodecs.codecs.isEmpty() ? blocks : - new TypeLimitedBlockstore(blocks, config.datastore.allowedCodecs.codecs) + new TypeLimitedBlockstore(blocks, config.datastore.allowedCodecs.codecs); } public static Blockstore filteredBlockStore(Blockstore blocks, Config config) { From 379f066d7c30091d55b6ea0f91f3fbfe7bec1eb1 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 12 Oct 2023 22:35:54 +0100 Subject: [PATCH 057/218] Tolerate exceptions in ipns publishing and resolving Allow early exit from ipns resolution --- .../org/peergos/protocol/dht/Kademlia.java | 29 ++++++----- src/test/java/org/peergos/KademliaTest.java | 49 +++++++++++++++++++ 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 1cfe0ffa..d85bece0 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -300,19 +300,22 @@ public CompletableFuture publishIpnsValue(PrivKey priv, Multihash publishe long ttl = hours * 3600_000_000_000L; int publishes = 0; - while (publishes < 20) { - List closestPeers = findClosestPeers(publisher, 20, us); + for (int i=0; i < 5 && publishes < 20; i++) { + List closestPeers = findClosestPeers(publisher, 25, us); for (PeerAddresses peer : closestPeers) { - boolean success = dialPeer(peer, us).join().putValue("/ipfs/" + value, expiry, sequence, - ttl, publisher, priv).join(); - if (success) - publishes++; + try { + boolean success = dialPeer(peer, us).join() + .putValue("/ipfs/" + value, expiry, sequence, + ttl, publisher, priv).join(); + if (success) + publishes++; + } catch (Exception e) {} } } return CompletableFuture.completedFuture(null); } - public CompletableFuture resolveIpnsValue(Multihash publisher, Host us) { + public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, int minResults) { List closestPeers = findClosestPeers(publisher, 20, us); List candidates = new ArrayList<>(); Set queryCandidates = new HashSet<>(); @@ -321,10 +324,14 @@ public CompletableFuture resolveIpnsValue(Multihash publisher, Host us) if (queriedPeers.contains(peer.peerId)) continue; queriedPeers.add(peer.peerId); - GetResult res = dialPeer(peer, us).join().getValue(publisher).join(); - if (res.record.isPresent() && res.record.get().publisher.equals(publisher)) - candidates.add(res.record.get().value); - queryCandidates.addAll(res.closerPeers); + try { + GetResult res = dialPeer(peer, us).join().getValue(publisher).join(); + if (res.record.isPresent() && res.record.get().publisher.equals(publisher)) + candidates.add(res.record.get().value); + queryCandidates.addAll(res.closerPeers); + } catch (Exception e) {} + if (candidates.size() >= minResults) + break; } // Validate and sort records by sequence number diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 059a4d3f..67737603 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -3,6 +3,8 @@ import io.ipfs.cid.*; import io.ipfs.multihash.Multihash; import io.libp2p.core.*; +import io.libp2p.core.crypto.*; +import io.libp2p.crypto.keys.*; import org.junit.*; import org.peergos.blockstore.*; import org.peergos.protocol.*; @@ -52,4 +54,51 @@ public void findOtherNode() throws Exception { node2.stop(); } } + + @Test + public void ipnsBenchmark() throws Exception { + RamBlockstore blockstore1 = new RamBlockstore(); + HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), + new RamProviderStore(), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + Host node1 = builder1.build(); + node1.start().join(); + IdentifyBuilder.addIdentifyProtocol(node1); + + HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), + new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + Host node2 = builder2.build(); + node2.start().join(); + IdentifyBuilder.addIdentifyProtocol(node2); + + Cid value = blockstore1.put("Provide me.".getBytes(), Cid.Codec.Raw).join(); + + try { + // bootstrap node 1 + Kademlia dht1 = builder1.getWanDht().get(); + dht1.bootstrapRoutingTable(node1, BootstrapTest.BOOTSTRAP_NODES, addr -> !addr.contains("/wss/")); + dht1.bootstrap(node1); + + // bootstrap node 2 + Kademlia dht2 = builder2.getWanDht().get(); + dht2.bootstrapRoutingTable(node2, BootstrapTest.BOOTSTRAP_NODES, addr -> !addr.contains("/wss/")); + dht2.bootstrap(node2); + + for (int i=0; i < 10; i++) { + // publish mapping from node 1 + PrivKey signer = Ed25519Kt.generateEd25519KeyPair().getFirst(); + Multihash pub = Multihash.deserialize(PeerId.fromPubKey(signer.publicKey()).getBytes()); + dht1.publishIpnsValue(signer, pub, value, 1, node1).join(); + + // retrieve it from node 2 + long t0 = System.currentTimeMillis(); + String res = dht2.resolveIpnsValue(pub, node2, 1).join(); + long t1 = System.currentTimeMillis(); + Assert.assertTrue(res.equals("/ipfs/" + value)); + System.out.println("Resolved in " + (t1 - t0) + "ms"); + } + } finally { + node1.stop(); + node2.stop(); + } + } } From 9188ef0a193072195e2cd28348c5237e92635397 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 13 Oct 2023 10:04:50 +0100 Subject: [PATCH 058/218] Add timeout in test --- src/test/java/org/peergos/KademliaTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 67737603..760b3c51 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -91,7 +91,7 @@ public void ipnsBenchmark() throws Exception { // retrieve it from node 2 long t0 = System.currentTimeMillis(); - String res = dht2.resolveIpnsValue(pub, node2, 1).join(); + String res = dht2.resolveIpnsValue(pub, node2, 1).orTimeout(10, TimeUnit.SECONDS).join(); long t1 = System.currentTimeMillis(); Assert.assertTrue(res.equals("/ipfs/" + value)); System.out.println("Resolved in " + (t1 - t0) + "ms"); From 2f46b2155f3d63fe460fde19d4a872abd6c76fb9 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 13 Oct 2023 12:05:47 +0100 Subject: [PATCH 059/218] Ignore flakey test --- src/test/java/org/peergos/KademliaTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 760b3c51..1416fefd 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -55,6 +55,7 @@ public void findOtherNode() throws Exception { } } + @Ignore @Test public void ipnsBenchmark() throws Exception { RamBlockstore blockstore1 = new RamBlockstore(); From 6d07ca61dbaf22a6ae9d10fc77ec90c44d7a33b9 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 13 Oct 2023 16:39:28 +0100 Subject: [PATCH 060/218] Log ipns publish times in benchmark --- src/test/java/org/peergos/KademliaTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 1416fefd..e29ee6a2 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -84,11 +84,16 @@ public void ipnsBenchmark() throws Exception { dht2.bootstrapRoutingTable(node2, BootstrapTest.BOOTSTRAP_NODES, addr -> !addr.contains("/wss/")); dht2.bootstrap(node2); + List signers = new ArrayList<>(); for (int i=0; i < 10; i++) { // publish mapping from node 1 PrivKey signer = Ed25519Kt.generateEd25519KeyPair().getFirst(); + signers.add(signer); Multihash pub = Multihash.deserialize(PeerId.fromPubKey(signer.publicKey()).getBytes()); + long p0 = System.currentTimeMillis(); dht1.publishIpnsValue(signer, pub, value, 1, node1).join(); + long p1 = System.currentTimeMillis(); + System.out.println("Publish took " + (p1-p0) + "ms"); // retrieve it from node 2 long t0 = System.currentTimeMillis(); @@ -97,6 +102,16 @@ public void ipnsBenchmark() throws Exception { Assert.assertTrue(res.equals("/ipfs/" + value)); System.out.println("Resolved in " + (t1 - t0) + "ms"); } + + // retrieve all again + for (PrivKey signer : signers) { + Multihash pub = Multihash.deserialize(PeerId.fromPubKey(signer.publicKey()).getBytes()); + long t0 = System.currentTimeMillis(); + String res = dht2.resolveIpnsValue(pub, node2, 1).orTimeout(10, TimeUnit.SECONDS).join(); + long t1 = System.currentTimeMillis(); + Assert.assertTrue(res.equals("/ipfs/" + value)); + System.out.println("Resolved again in " + (t1 - t0) + "ms"); + } } finally { node1.stop(); node2.stop(); From b4db97b08175bc7e45bef52c26b1800f79d3f406 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 13 Oct 2023 21:08:20 +0100 Subject: [PATCH 061/218] Reduce impedance mismatch between MultiAddress and Multiaddr Kademlia was doing loads of conversions.. --- src/main/java/org/peergos/PeerAddresses.java | 47 ++++++++++++++----- .../java/org/peergos/client/NabuClient.java | 3 +- .../protocol/autonat/AutonatProtocol.java | 2 +- .../org/peergos/protocol/dht/Kademlia.java | 5 +- .../peergos/protocol/dht/KademliaEngine.java | 12 ++--- src/test/java/org/peergos/IpnsTest.java | 2 +- src/test/java/org/peergos/ProvideTest.java | 1 - 7 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/peergos/PeerAddresses.java b/src/main/java/org/peergos/PeerAddresses.java index e5c3323a..5954505e 100644 --- a/src/main/java/org/peergos/PeerAddresses.java +++ b/src/main/java/org/peergos/PeerAddresses.java @@ -4,31 +4,59 @@ import io.ipfs.cid.*; import io.ipfs.multiaddr.*; import io.ipfs.multihash.*; +import io.ipfs.multihash.Multihash; import io.libp2p.core.*; +import io.libp2p.core.multiformats.*; +import io.libp2p.core.multiformats.Protocol; import org.peergos.protocol.dht.pb.*; import java.io.*; +import java.net.*; import java.util.*; import java.util.stream.*; public class PeerAddresses { public final Multihash peerId; - public final List addresses; + public final List addresses; - public PeerAddresses(Multihash peerId, List addresses) { + public PeerAddresses(Multihash peerId, List addresses) { this.peerId = peerId; this.addresses = addresses; } - public List getPublicAddresses() { - return addresses.stream().filter(a -> a.isPublic(false)).collect(Collectors.toList()); + public List getPublicAddresses() { + return addresses.stream().filter(a -> isPublic(a, false)).collect(Collectors.toList()); + } + + public static boolean isPublic(Multiaddr a, boolean testReachable) { + if (a.has(Protocol.IP6ZONE)) + return true; + try { + if (a.has(Protocol.IP4)) { + InetAddress ip = InetAddress.getByName(a.getFirstComponent(Protocol.IP4).getStringValue()); + if (!ip.isLoopbackAddress() && !ip.isSiteLocalAddress() && !ip.isLinkLocalAddress() && !ip.isAnyLocalAddress()) { + return !testReachable || ip.isReachable(1000); + } + + return false; + } + if (a.has(Protocol.IP6)) { + InetAddress ip = InetAddress.getByName(a.getFirstComponent(Protocol.IP6).getStringValue()); + if (!ip.isLoopbackAddress() && !ip.isSiteLocalAddress() && !ip.isLinkLocalAddress() && !ip.isAnyLocalAddress()) { + return !testReachable || ip.isReachable(1000); + } + + return false; + } + } catch (IOException e) {} + return false; } public static PeerAddresses fromProtobuf(Dht.Message.Peer peer) { Multihash peerId = Multihash.deserialize(peer.getId().toByteArray()); - List addrs = peer.getAddrsList() + List addrs = peer.getAddrsList() .stream() - .map(b -> new MultiAddress(b.toByteArray())) + .map(b -> Multiaddr.deserialize(b.toByteArray())) .collect(Collectors.toList()); return new PeerAddresses(peerId, addrs); } @@ -37,17 +65,14 @@ public Dht.Message.Peer toProtobuf() { return Dht.Message.Peer.newBuilder() .setId(ByteString.copyFrom(peerId.toBytes())) .addAllAddrs(addresses.stream() - .map(a -> ByteString.copyFrom(a.getBytes())) + .map(a -> ByteString.copyFrom(a.serialize())) .collect(Collectors.toList())) .build(); } public static PeerAddresses fromHost(Host host) { Multihash peerId = Multihash.deserialize(host.getPeerId().getBytes()); - List addrs = host.listenAddresses() - .stream() - .map(b -> new MultiAddress(b.serialize())) - .collect(Collectors.toList()); + List addrs = host.listenAddresses(); return new PeerAddresses(peerId, addrs); } diff --git a/src/main/java/org/peergos/client/NabuClient.java b/src/main/java/org/peergos/client/NabuClient.java index 03014958..ea180aaa 100644 --- a/src/main/java/org/peergos/client/NabuClient.java +++ b/src/main/java/org/peergos/client/NabuClient.java @@ -4,6 +4,7 @@ import io.ipfs.multiaddr.MultiAddress; import io.ipfs.multihash.Multihash; import io.libp2p.core.PeerId; +import io.libp2p.core.multiformats.*; import org.peergos.PeerAddresses; import org.peergos.util.JSONParser; @@ -142,7 +143,7 @@ public List findProviders(Multihash hash) throws IOException { Map responses = (Map)entry.get("Responses"); Multihash peerId = Multihash.fromBase58((String) responses.get("ID")); ArrayList addrs = (ArrayList)responses.get("Addrs"); - List peerAddresses = addrs.stream().map(a -> new MultiAddress(a)).collect(Collectors.toList()); + List peerAddresses = addrs.stream().map(a -> Multiaddr.fromString(a)).collect(Collectors.toList()); providers.add(new PeerAddresses(peerId, peerAddresses)); } return providers; diff --git a/src/main/java/org/peergos/protocol/autonat/AutonatProtocol.java b/src/main/java/org/peergos/protocol/autonat/AutonatProtocol.java index 56d5a08f..94c6fcdc 100644 --- a/src/main/java/org/peergos/protocol/autonat/AutonatProtocol.java +++ b/src/main/java/org/peergos/protocol/autonat/AutonatProtocol.java @@ -33,7 +33,7 @@ default CompletableFuture requestDial(PeerAddresse .setDial(Autonat.Message.Dial.newBuilder() .setPeer(Autonat.Message.PeerInfo.newBuilder() .addAllAddrs(us.addresses.stream() - .map(a -> ByteString.copyFrom(a.getBytes())) + .map(a -> ByteString.copyFrom(a.serialize())) .collect(Collectors.toList())) .setId(ByteString.copyFrom(us.peerId.toBytes())))) .build()) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index d85bece0..46de5af1 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -140,7 +140,7 @@ public List findClosestPeers(Multihash peerIdkey, int maxCount, H if (maxCount == 1) { Collection existing = addressBook.get(PeerId.fromBase58(peerIdkey.toBase58())).join(); if (! existing.isEmpty()) - return Collections.singletonList(new PeerAddresses(peerIdkey, existing.stream().map(a -> a.toString()).map(MultiAddress::new).collect(Collectors.toList()))); + return Collections.singletonList(new PeerAddresses(peerIdkey, new ArrayList<>(existing))); Optional match = localClosest.stream().filter(p -> p.peerId.equals(peerIdkey)).findFirst(); if (match.isPresent()) return Collections.singletonList(match.get()); @@ -266,8 +266,7 @@ else if (e.getCause() instanceof ConnectionClosedException) {} private Multiaddr[] getPublic(PeerAddresses target) { return target.addresses.stream() - .filter(a -> localDht || a.isPublic(false)) - .map(a -> Multiaddr.fromString(a.toString())) + .filter(a -> localDht || PeerAddresses.isPublic(a, false)) .collect(Collectors.toList()).toArray(new Multiaddr[0]); } diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 1622914d..331b6c9b 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -58,10 +58,7 @@ public List getKClosestPeers(byte[] key) { } return nodes.stream() .map(n -> { - List addrs = addressBook.getAddrs(PeerId.fromBase58(n.getLink())).join() - .stream() - .map(m -> new MultiAddress(m.toString())) - .collect(Collectors.toList()); + List addrs = new ArrayList<>(addressBook.getAddrs(PeerId.fromBase58(n.getLink())).join()); return new PeerAddresses(Multihash.fromBase58(n.getLink()), addrs); }) .collect(Collectors.toList()); @@ -107,11 +104,8 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { Set providers = providersStore.getProviders(hash); if (blocks.hasAny(hash).join()) { providers = new HashSet<>(providers); - providers.add(new PeerAddresses(ourPeerId, addressBook.getAddrs(PeerId.fromBase58(ourPeerId.toBase58())) - .join() - .stream() - .map(a -> new MultiAddress(a.toString())) - .collect(Collectors.toList()))); + providers.add(new PeerAddresses(ourPeerId, + new ArrayList<>(addressBook.getAddrs(PeerId.fromBase58(ourPeerId.toBase58())).join()))); } Dht.Message.Builder builder = msg.toBuilder(); builder = builder.addAllProviderPeers(providers.stream() diff --git a/src/test/java/org/peergos/IpnsTest.java b/src/test/java/org/peergos/IpnsTest.java index 7b65cadc..6aab6154 100644 --- a/src/test/java/org/peergos/IpnsTest.java +++ b/src/test/java/org/peergos/IpnsTest.java @@ -93,7 +93,7 @@ public void retrieveKuboPublishedIPNS() throws IOException { break; PeerAddresses closer = queue.poll(); List candidates = closer.addresses.stream() - .map(MultiAddress::toString) + .map(a -> a.toString()) .filter(a -> a.contains("tcp") && a.contains("ip4") && !a.contains("127.0.0.1") && !a.contains("/172.")) .collect(Collectors.toList()); for (String candidate: candidates) { diff --git a/src/test/java/org/peergos/ProvideTest.java b/src/test/java/org/peergos/ProvideTest.java index 61db672c..3e2650b7 100644 --- a/src/test/java/org/peergos/ProvideTest.java +++ b/src/test/java/org/peergos/ProvideTest.java @@ -56,7 +56,6 @@ public void provideBlock() { byte[] blockData = ("This is hopefully a unique block" + System.currentTimeMillis()).getBytes(); Cid block = blockstore.put(blockData, Cid.Codec.Raw).join(); PeerAddresses ourAddresses = new PeerAddresses(node1Id, node1.listenAddresses().stream() - .map(m -> new MultiAddress(m.toString())) .collect(Collectors.toList())); dht.provideBlock(block, node1, ourAddresses).join(); From 916b9f87fdc26dfe1dfe48f6524f252311fe8810 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 14 Oct 2023 00:27:27 +0100 Subject: [PATCH 062/218] Parallelise IPNS put calls This makes the 2nd half of publishing 10x faster --- src/main/java/org/peergos/protocol/dht/Kademlia.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 46de5af1..2649920f 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -301,15 +301,16 @@ public CompletableFuture publishIpnsValue(PrivKey priv, Multihash publishe int publishes = 0; for (int i=0; i < 5 && publishes < 20; i++) { List closestPeers = findClosestPeers(publisher, 25, us); - for (PeerAddresses peer : closestPeers) { + publishes += closestPeers.stream().parallel().mapToInt(peer -> { try { boolean success = dialPeer(peer, us).join() .putValue("/ipfs/" + value, expiry, sequence, ttl, publisher, priv).join(); if (success) - publishes++; + return 1; } catch (Exception e) {} - } + return 0; + }).sum(); } return CompletableFuture.completedFuture(null); } From 22c54f3406fddb88ff7b64be7402fb3b46916fb1 Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 15 Oct 2023 12:40:53 +0100 Subject: [PATCH 063/218] Limit size of RamProviderStore --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 +- src/main/java/org/peergos/RamAddressBook.java | 14 +------------- .../peergos/protocol/dht/RamProviderStore.java | 7 ++++++- src/main/java/org/peergos/util/LRUCache.java | 16 ++++++++++++++++ src/test/java/org/peergos/BitswapMirrorTest.java | 2 +- src/test/java/org/peergos/BitswapTest.java | 4 ++-- src/test/java/org/peergos/BootstrapTest.java | 2 +- src/test/java/org/peergos/FindPeerTest.java | 2 +- src/test/java/org/peergos/FindProviderTest.java | 2 +- src/test/java/org/peergos/HandlerTest.java | 2 +- src/test/java/org/peergos/HttpProxyTest.java | 6 +++--- src/test/java/org/peergos/IpnsTest.java | 4 ++-- src/test/java/org/peergos/KademliaTest.java | 8 ++++---- .../java/org/peergos/KuboFindProviderTest.java | 2 +- src/test/java/org/peergos/P2pHttpChatTest.java | 4 ++-- src/test/java/org/peergos/P2pHttpTest.java | 4 ++-- src/test/java/org/peergos/ProvideTest.java | 2 +- src/test/java/org/peergos/RelayTest.java | 4 ++-- 18 files changed, 48 insertions(+), 39 deletions(-) create mode 100644 src/main/java/org/peergos/util/LRUCache.java diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 40ae7bd3..474a09af 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -182,7 +182,7 @@ public static EmbeddedIpfs build(RecordStore records, Blockstore blockstore = provideBlocks ? new ProvidingBlockstore(blocks) : blocks; - ProviderStore providers = new RamProviderStore(); + ProviderStore providers = new RamProviderStore(10_000); HostBuilder builder = new HostBuilder().setIdentity(identity.privKeyProtobuf).listen(swarmAddresses); if (! builder.getPeerId().equals(identity.peerId)) { diff --git a/src/main/java/org/peergos/RamAddressBook.java b/src/main/java/org/peergos/RamAddressBook.java index 9ef35429..1d157272 100644 --- a/src/main/java/org/peergos/RamAddressBook.java +++ b/src/main/java/org/peergos/RamAddressBook.java @@ -3,6 +3,7 @@ import io.libp2p.core.*; import io.libp2p.core.multiformats.*; import org.jetbrains.annotations.*; +import org.peergos.util.*; import java.util.*; import java.util.concurrent.*; @@ -40,17 +41,4 @@ public CompletableFuture setAddrs(@NotNull PeerId peerId, long ttl, @NotNu addresses.put(peerId, val); return CompletableFuture.completedFuture(null); } - - public static class LRUCache extends LinkedHashMap { - private final int cacheSize; - - public LRUCache(int cacheSize) { - super(16, 0.75f, true); - this.cacheSize = cacheSize; - } - - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() >= cacheSize; - } - } } diff --git a/src/main/java/org/peergos/protocol/dht/RamProviderStore.java b/src/main/java/org/peergos/protocol/dht/RamProviderStore.java index ac939079..b36db61c 100644 --- a/src/main/java/org/peergos/protocol/dht/RamProviderStore.java +++ b/src/main/java/org/peergos/protocol/dht/RamProviderStore.java @@ -2,12 +2,17 @@ import io.ipfs.multihash.*; import org.peergos.*; +import org.peergos.util.*; import java.util.*; public class RamProviderStore implements ProviderStore { - private final Map> store = new HashMap<>(); + private final Map> store; + + public RamProviderStore(int cacheSize) { + store = new LRUCache<>(cacheSize); + } @Override public synchronized void addProvider(Multihash m, PeerAddresses peer) { diff --git a/src/main/java/org/peergos/util/LRUCache.java b/src/main/java/org/peergos/util/LRUCache.java new file mode 100644 index 00000000..36ed6c97 --- /dev/null +++ b/src/main/java/org/peergos/util/LRUCache.java @@ -0,0 +1,16 @@ +package org.peergos.util; + +import java.util.*; + +public class LRUCache extends LinkedHashMap { + private final int cacheSize; + + public LRUCache(int cacheSize) { + super(16, 0.75f, true); + this.cacheSize = cacheSize; + } + + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() >= cacheSize; + } +} diff --git a/src/test/java/org/peergos/BitswapMirrorTest.java b/src/test/java/org/peergos/BitswapMirrorTest.java index 0d4ec268..0c29e890 100644 --- a/src/test/java/org/peergos/BitswapMirrorTest.java +++ b/src/test/java/org/peergos/BitswapMirrorTest.java @@ -23,7 +23,7 @@ public class BitswapMirrorTest { //@Ignore // Local testing only for now - run this prior: ./ipfs pin add zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg public void mirrorTree() throws IOException { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/BitswapTest.java b/src/test/java/org/peergos/BitswapTest.java index 6f81aa54..b229af87 100644 --- a/src/test/java/org/peergos/BitswapTest.java +++ b/src/test/java/org/peergos/BitswapTest.java @@ -18,11 +18,11 @@ public class BitswapTest { @Test public void getBlock() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); RamBlockstore blockstore2 = new RamBlockstore(); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), blockstore2, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node1.start().join(); node2.start().join(); diff --git a/src/test/java/org/peergos/BootstrapTest.java b/src/test/java/org/peergos/BootstrapTest.java index 2e8dfa25..68a034fc 100644 --- a/src/test/java/org/peergos/BootstrapTest.java +++ b/src/test/java/org/peergos/BootstrapTest.java @@ -37,7 +37,7 @@ public class BootstrapTest { @Test public void bootstrap() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/FindPeerTest.java b/src/test/java/org/peergos/FindPeerTest.java index 0227490f..7a524938 100644 --- a/src/test/java/org/peergos/FindPeerTest.java +++ b/src/test/java/org/peergos/FindPeerTest.java @@ -17,7 +17,7 @@ public class FindPeerTest { public void findLongRunningNode() { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/FindProviderTest.java b/src/test/java/org/peergos/FindProviderTest.java index c8f02c43..958df46b 100644 --- a/src/test/java/org/peergos/FindProviderTest.java +++ b/src/test/java/org/peergos/FindProviderTest.java @@ -20,7 +20,7 @@ public class FindProviderTest { public void findBlockProvider() { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); diff --git a/src/test/java/org/peergos/HandlerTest.java b/src/test/java/org/peergos/HandlerTest.java index 1b2ed6c4..afd81613 100644 --- a/src/test/java/org/peergos/HandlerTest.java +++ b/src/test/java/org/peergos/HandlerTest.java @@ -82,7 +82,7 @@ public void codecTest() { public void findBlockProviderTest() throws IOException { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); HttpServer apiServer = null; diff --git a/src/test/java/org/peergos/HttpProxyTest.java b/src/test/java/org/peergos/HttpProxyTest.java index 1e875d09..fd22ddb1 100644 --- a/src/test/java/org/peergos/HttpProxyTest.java +++ b/src/test/java/org/peergos/HttpProxyTest.java @@ -34,12 +34,12 @@ public void peerid() { public void p2pProxyRequest() throws IOException { InetSocketAddress unusedProxyTarget = new InetSocketAddress("127.0.0.1", 7000); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(new HttpProtocol.Binding(unusedProxyTarget)); Host node1 = builder1.build(); InetSocketAddress proxyTarget = new InetSocketAddress("127.0.0.1", TestPorts.getPort()); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(new HttpProtocol.Binding(proxyTarget)); Host node2 = builder2.build(); node1.start().join(); @@ -95,7 +95,7 @@ public void p2pProxyRequest() throws IOException { public void p2pProxyClientTest() throws Exception { InetSocketAddress unusedProxyTarget = new InetSocketAddress("127.0.0.1", 7000); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(new HttpProtocol.Binding(unusedProxyTarget)); Host node1 = builder1.build(); node1.start().join(); diff --git a/src/test/java/org/peergos/IpnsTest.java b/src/test/java/org/peergos/IpnsTest.java index 6aab6154..9d9d2862 100644 --- a/src/test/java/org/peergos/IpnsTest.java +++ b/src/test/java/org/peergos/IpnsTest.java @@ -24,7 +24,7 @@ public class IpnsTest { public void publishIPNSRecordToKubo() throws IOException { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); @@ -70,7 +70,7 @@ public void publishIPNSRecordToKubo() throws IOException { public void retrieveKuboPublishedIPNS() throws IOException { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index e29ee6a2..9e196d44 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -19,13 +19,13 @@ public class KademliaTest { public void findOtherNode() throws Exception { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node2.start().join(); IdentifyBuilder.addIdentifyProtocol(node2); @@ -60,13 +60,13 @@ public void findOtherNode() throws Exception { public void ipnsBenchmark() throws Exception { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node2.start().join(); IdentifyBuilder.addIdentifyProtocol(node2); diff --git a/src/test/java/org/peergos/KuboFindProviderTest.java b/src/test/java/org/peergos/KuboFindProviderTest.java index c0949957..fb8c3a77 100644 --- a/src/test/java/org/peergos/KuboFindProviderTest.java +++ b/src/test/java/org/peergos/KuboFindProviderTest.java @@ -19,7 +19,7 @@ public class KuboFindProviderTest { @Test public void findProviderOverYamux() throws IOException { - HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(), + HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) .addMuxers(List.of(StreamMuxerProtocol.getYamux())); Host node1 = builder1.build(); diff --git a/src/test/java/org/peergos/P2pHttpChatTest.java b/src/test/java/org/peergos/P2pHttpChatTest.java index 0d28a408..eb4de071 100644 --- a/src/test/java/org/peergos/P2pHttpChatTest.java +++ b/src/test/java/org/peergos/P2pHttpChatTest.java @@ -25,7 +25,7 @@ public void p2pHttpChat() { h.accept(replyOk.retain()); }); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(node1Http); Host node1 = builder1.build(); HttpProtocol.Binding node2Http = new HttpProtocol.Binding((s, req, h) -> { @@ -34,7 +34,7 @@ public void p2pHttpChat() { h.accept(replyOk.retain()); }); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(node2Http); Host node2 = builder2.build(); node1.start().join(); diff --git a/src/test/java/org/peergos/P2pHttpTest.java b/src/test/java/org/peergos/P2pHttpTest.java index 2e2e1956..2d54c193 100644 --- a/src/test/java/org/peergos/P2pHttpTest.java +++ b/src/test/java/org/peergos/P2pHttpTest.java @@ -37,7 +37,7 @@ public void p2pTest() { h.accept(emptyReply.retain()); }); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); builder1 = builder1.addProtocol(node1Http); Host node1 = builder1.build(); node1.start().join(); @@ -73,7 +73,7 @@ public void p2pTest() { HttpProtocol.proxyRequest(req, new InetSocketAddress("127.0.0.1", localPort), h); }); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), blockstore2, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, b, p, a) -> CompletableFuture.completedFuture(true)); builder2 = builder2.addProtocol(node2Http); Host node2 = builder2.build(); node2.start().join(); diff --git a/src/test/java/org/peergos/ProvideTest.java b/src/test/java/org/peergos/ProvideTest.java index 3e2650b7..13f46a7b 100644 --- a/src/test/java/org/peergos/ProvideTest.java +++ b/src/test/java/org/peergos/ProvideTest.java @@ -20,7 +20,7 @@ public class ProvideTest { public void provideBlock() { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); Multihash node1Id = Multihash.deserialize(node1.getPeerId().getBytes()); diff --git a/src/test/java/org/peergos/RelayTest.java b/src/test/java/org/peergos/RelayTest.java index 813d483e..87c25d3f 100644 --- a/src/test/java/org/peergos/RelayTest.java +++ b/src/test/java/org/peergos/RelayTest.java @@ -19,11 +19,11 @@ public class RelayTest { @Ignore // needs fixed find providers public void relay() { HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); HostBuilder builder2 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node2.start().join(); From 314467d799980983587de61dd066f189b71699fa Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 15 Oct 2023 17:12:10 +0100 Subject: [PATCH 064/218] Log averages in IPNS benchmark --- src/test/java/org/peergos/KademliaTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 9e196d44..07d93720 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -85,7 +85,9 @@ public void ipnsBenchmark() throws Exception { dht2.bootstrap(node2); List signers = new ArrayList<>(); - for (int i=0; i < 10; i++) { + long publishTotal = 0, resolveTotal = 0; + int iterations = 10; + for (int i = 0; i < iterations; i++) { // publish mapping from node 1 PrivKey signer = Ed25519Kt.generateEd25519KeyPair().getFirst(); signers.add(signer); @@ -94,6 +96,7 @@ public void ipnsBenchmark() throws Exception { dht1.publishIpnsValue(signer, pub, value, 1, node1).join(); long p1 = System.currentTimeMillis(); System.out.println("Publish took " + (p1-p0) + "ms"); + publishTotal += p1-p0; // retrieve it from node 2 long t0 = System.currentTimeMillis(); @@ -101,7 +104,9 @@ public void ipnsBenchmark() throws Exception { long t1 = System.currentTimeMillis(); Assert.assertTrue(res.equals("/ipfs/" + value)); System.out.println("Resolved in " + (t1 - t0) + "ms"); + resolveTotal += t1-t0; } + System.out.println("Publish av: " + publishTotal/iterations + ", resolve av: " + resolveTotal/iterations); // retrieve all again for (PrivKey signer : signers) { From 39f12baa5d2fdfa8668203197f07f508269f3361 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 16 Oct 2023 10:12:39 +0100 Subject: [PATCH 065/218] Simplify kademlia lookup --- .../org/peergos/protocol/dht/Kademlia.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 2649920f..c0579d95 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -134,8 +134,8 @@ private int compareKeys(RoutingEntry a, RoutingEntry b, Id keyId) { public List findClosestPeers(Multihash peerIdkey, int maxCount, Host us) { byte[] key = peerIdkey.toBytes(); Id keyId = Id.create(Hash.sha256(key), 256); - SortedSet closest = new TreeSet<>((a, b) -> compareKeys(a, b, keyId)); - SortedSet toQuery = new TreeSet<>((a, b) -> compareKeys(a, b, keyId)); + SortedSet closest = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); + SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); List localClosest = engine.getKClosestPeers(key); if (maxCount == 1) { Collection existing = addressBook.get(PeerId.fromBase58(peerIdkey.toBase58())).join(); @@ -149,15 +149,17 @@ public List findClosestPeers(Multihash peerIdkey, int maxCount, H .map(p -> new RoutingEntry(Id.create(Hash.sha256(p.peerId.toBytes()), 256), p)) .collect(Collectors.toList())); toQuery.addAll(closest); - Set queried = new HashSet<>(); + Set queried = Collections.synchronizedSet(new HashSet<>()); int queryParallelism = 3; while (true) { - List queryThisRound = toQuery.stream().limit(queryParallelism).collect(Collectors.toList()); - toQuery.removeAll(queryThisRound); - queryThisRound.forEach(r -> queried.add(r.addresses.peerId)); - List>> futures = queryThisRound.stream() + List>> futures = toQuery.stream() + .limit(queryParallelism) .parallel() - .map(r -> getCloserPeers(peerIdkey, r.addresses, us)) + .map(r -> { + toQuery.remove(r); + queried.add(r.addresses.peerId); + return getCloserPeers(peerIdkey, r.addresses, us); + }) .collect(Collectors.toList()); boolean foundCloser = false; for (CompletableFuture> future : futures) { From 2a576b9708272521beec7ac35a235b5a4e8088ca Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 16 Oct 2023 13:46:23 +0100 Subject: [PATCH 066/218] Store provider records in raw protobuf to avoid serialization --- .../org/peergos/protocol/dht/KademliaEngine.java | 12 +++++++----- .../java/org/peergos/protocol/dht/ProviderStore.java | 6 +++--- .../org/peergos/protocol/dht/RamProviderStore.java | 10 +++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 331b6c9b..499b6119 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -47,7 +47,10 @@ public synchronized void addIncomingConnection(PeerId peer, Multiaddr addr) { } public Set getProviders(Multihash h) { - return providersStore.getProviders(h); + return providersStore.getProviders(h) + .stream() + .map(PeerAddresses::fromProtobuf) + .collect(Collectors.toSet()); } public List getKClosestPeers(byte[] key) { @@ -95,21 +98,20 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { byte[] remotePeerIdBytes = source.getBytes(); Multihash hash = Multihash.deserialize(msg.getKey().toByteArray()); if (providers.stream().allMatch(p -> Arrays.equals(p.getId().toByteArray(), remotePeerIdBytes))) { - providers.stream().map(PeerAddresses::fromProtobuf).forEach(p -> providersStore.addProvider(hash, p)); + providers.forEach(p -> providersStore.addProvider(hash, p)); } break; } case GET_PROVIDERS: { Multihash hash = Multihash.deserialize(msg.getKey().toByteArray()); - Set providers = providersStore.getProviders(hash); + Set providers = providersStore.getProviders(hash); if (blocks.hasAny(hash).join()) { providers = new HashSet<>(providers); providers.add(new PeerAddresses(ourPeerId, - new ArrayList<>(addressBook.getAddrs(PeerId.fromBase58(ourPeerId.toBase58())).join()))); + new ArrayList<>(addressBook.getAddrs(PeerId.fromBase58(ourPeerId.toBase58())).join())).toProtobuf()); } Dht.Message.Builder builder = msg.toBuilder(); builder = builder.addAllProviderPeers(providers.stream() - .map(PeerAddresses::toProtobuf) .collect(Collectors.toList())); builder = builder.addAllCloserPeers(getKClosestPeers(msg.getKey().toByteArray()) .stream() diff --git a/src/main/java/org/peergos/protocol/dht/ProviderStore.java b/src/main/java/org/peergos/protocol/dht/ProviderStore.java index 957499a6..028d2198 100644 --- a/src/main/java/org/peergos/protocol/dht/ProviderStore.java +++ b/src/main/java/org/peergos/protocol/dht/ProviderStore.java @@ -1,13 +1,13 @@ package org.peergos.protocol.dht; import io.ipfs.multihash.*; -import org.peergos.*; +import org.peergos.protocol.dht.pb.*; import java.util.*; public interface ProviderStore { - void addProvider(Multihash m, PeerAddresses peer); + void addProvider(Multihash m, Dht.Message.Peer peer); - Set getProviders(Multihash m); + Set getProviders(Multihash m); } diff --git a/src/main/java/org/peergos/protocol/dht/RamProviderStore.java b/src/main/java/org/peergos/protocol/dht/RamProviderStore.java index b36db61c..e4e1d099 100644 --- a/src/main/java/org/peergos/protocol/dht/RamProviderStore.java +++ b/src/main/java/org/peergos/protocol/dht/RamProviderStore.java @@ -1,27 +1,27 @@ package org.peergos.protocol.dht; -import io.ipfs.multihash.*; -import org.peergos.*; +import io.ipfs.multihash.Multihash; +import org.peergos.protocol.dht.pb.*; import org.peergos.util.*; import java.util.*; public class RamProviderStore implements ProviderStore { - private final Map> store; + private final Map> store; public RamProviderStore(int cacheSize) { store = new LRUCache<>(cacheSize); } @Override - public synchronized void addProvider(Multihash m, PeerAddresses peer) { + public synchronized void addProvider(Multihash m, Dht.Message.Peer peer) { store.putIfAbsent(m, new HashSet<>()); store.get(m).add(peer); } @Override - public synchronized Set getProviders(Multihash m) { + public synchronized Set getProviders(Multihash m) { return store.getOrDefault(m, Collections.emptySet()); } } From b517de1ea9ebece949a8fc7255daa4a297ec4f40 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 17 Oct 2023 10:02:31 +0100 Subject: [PATCH 067/218] Don't error if we try to delete a block we don't have --- src/main/java/org/peergos/net/APIHandler.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index b3b3898f..0dfae46e 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -124,19 +124,11 @@ public void handleCallToAPI(HttpExchange httpExchange) { throw new APIException("argument \"cid\" is required\n"); } Cid cid = Cid.decode(args.get(0)); - boolean deleted = ipfs.blockstore.rm(cid).join(); - if (deleted) { - Map res = new HashMap<>(); - res.put("Error", ""); - res.put("Hash", cid.toString()); - replyJson(httpExchange, JSONParser.toString(res)); - } else { - try { - httpExchange.sendResponseHeaders(400, 0); - } catch (IOException ioe) { - HttpUtil.replyError(httpExchange, ioe); - } - } + ipfs.blockstore.rm(cid).join(); + Map res = new HashMap<>(); + res.put("Error", ""); + res.put("Hash", cid.toString()); + replyJson(httpExchange, JSONParser.toString(res)); break; } case STAT: { // https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-block-stat From 7658f321ac3f54e83f4d21c7150236e360afec6d Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 17 Oct 2023 10:16:25 +0100 Subject: [PATCH 068/218] Revert change to not error when deleting a block we don't have. This is useful information downstream. --- src/main/java/org/peergos/net/APIHandler.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index 0dfae46e..b3b3898f 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -124,11 +124,19 @@ public void handleCallToAPI(HttpExchange httpExchange) { throw new APIException("argument \"cid\" is required\n"); } Cid cid = Cid.decode(args.get(0)); - ipfs.blockstore.rm(cid).join(); - Map res = new HashMap<>(); - res.put("Error", ""); - res.put("Hash", cid.toString()); - replyJson(httpExchange, JSONParser.toString(res)); + boolean deleted = ipfs.blockstore.rm(cid).join(); + if (deleted) { + Map res = new HashMap<>(); + res.put("Error", ""); + res.put("Hash", cid.toString()); + replyJson(httpExchange, JSONParser.toString(res)); + } else { + try { + httpExchange.sendResponseHeaders(400, 0); + } catch (IOException ioe) { + HttpUtil.replyError(httpExchange, ioe); + } + } break; } case STAT: { // https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-block-stat From c5c25c20ba7568a9434dd38171b9d7e90e4a7ce2 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 18 Oct 2023 10:16:43 +0100 Subject: [PATCH 069/218] Tolerate an inconsistent metadatadb that claims we have a block we don't have --- src/main/java/org/peergos/EmbeddedIpfs.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 474a09af..5c67263f 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -80,8 +80,16 @@ public List getBlocks(List wants, Set peers, boolean remote.add(w); } local.stream() - .map(w -> new HashedBlock(w.cid, blockstore.get(w.cid).join().get())) - .forEach(blocksFound::add); + .forEach(w -> { + try { + Optional block = blockstore.get(w.cid).join(); + block.ifPresent(b -> blocksFound.add(new HashedBlock(w.cid, b))); + if (block.isEmpty()) + remote.add(w); + } catch (Exception e) { + remote.add(w); + } + }); if (remote.isEmpty()) return blocksFound; return java.util.stream.Stream.concat( From 5257f0e9cd7dcc6b9e8c1cd6bea20261dea06107 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 18 Oct 2023 14:42:00 +0100 Subject: [PATCH 070/218] Log who sent us blocks we don't want --- src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index c30cc44c..eb9550df 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -178,7 +178,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { waiter.complete(new HashedBlock(c, data)); localWants.remove(w); } else - LOG.info("Received block we don't want: " + c); + LOG.info("Received block we don't want: " + c + " from " + sourcePeerId.bareMultihash()); } } catch (IOException e) { e.printStackTrace(); From 1a08f6fe5d082fb291ef0103534bd8610413e0cf Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 18 Oct 2023 15:22:14 +0100 Subject: [PATCH 071/218] Block repeated invalid auth attempts in bitswap Use metadb is S3.has() --- .../org/peergos/blockstore/s3/S3Blockstore.java | 4 +++- .../org/peergos/protocol/bitswap/Bitswap.java | 2 +- .../peergos/protocol/bitswap/BitswapEngine.java | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index 108a45fa..bb2b400b 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -224,7 +224,9 @@ private static V getWithBackoff(Supplier req) { @Override public CompletableFuture has(Cid cid) { - return getWithBackoff(() -> getSizeWithoutRetry(cid)).thenApply(optSize -> optSize.isPresent()); + if (blockMetadata.get(cid).isEmpty()) + return CompletableFuture.completedFuture(false); + return getWithBackoff(() -> getSizeWithoutRetry(cid)).thenApply(optSize -> optSize.isPresent()); } @Override diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index 36198f79..42acdbea 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -81,7 +81,7 @@ public Set getBroadcastAudience() { public void sendWants(Host us, Set peers) { Set wants = engine.getWants(); - LOG.fine("Broadcast wants: " + wants.size()); + LOG.info("Broadcast wants: " + wants.size()); Map haves = engine.getHaves(); // broadcast to all connected bitswap peers if none are supplied Set audience = peers.isEmpty() ? getBroadcastAudience() : peers; diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index eb9550df..466d3495 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -25,6 +25,7 @@ public class BitswapEngine { private final ConcurrentHashMap> localWants = new ConcurrentHashMap<>(); private final ConcurrentHashMap persistBlocks = new ConcurrentHashMap<>(); private final ConcurrentHashMap blockHaves = new ConcurrentHashMap<>(); + private final Map deniedWants = Collections.synchronizedMap(new LRUCache<>(10_000)); private final Set connections = new HashSet<>(); private final BlockRequestAuthoriser authoriser; private AddressBook addressBook; @@ -102,7 +103,18 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { boolean isCancel = e.getCancel(); boolean sendDontHave = e.getSendDontHave(); boolean wantBlock = e.getWantType().getNumber() == 0; + Want w = new Want(c, auth); if (wantBlock) { + boolean denied = deniedWants.containsKey(w); + if (denied) { + MessageOuterClass.Message.BlockPresence presence = MessageOuterClass.Message.BlockPresence.newBuilder() + .setCid(ByteString.copyFrom(c.toBytes())) + .setType(MessageOuterClass.Message.BlockPresenceType.DontHave) + .build(); + presences.add(presence); + messageSize += presence.getSerializedSize(); + continue; + } Optional block = store.get(c).join(); if (block.isPresent() && authoriser.allowRead(c, block.get(), sourcePeerId, auth.orElse("")).join()) { MessageOuterClass.Message.Block blockP = MessageOuterClass.Message.Block.newBuilder() @@ -121,6 +133,10 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { messageSize += blockSize; blocks.add(blockP); } else if (sendDontHave) { + if (block.isPresent()) { + deniedWants.put(w, true); + LOG.info("Rejecting auth for block " + c + " from " + sourcePeerId.bareMultihash()); + } MessageOuterClass.Message.BlockPresence presence = MessageOuterClass.Message.BlockPresence.newBuilder() .setCid(ByteString.copyFrom(c.toBytes())) .setType(MessageOuterClass.Message.BlockPresenceType.DontHave) From fd2f91b0e0a18e47131f7fd67fdfdddeb5433949 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 18 Oct 2023 16:07:06 +0100 Subject: [PATCH 072/218] Bitswap logging --- src/main/java/org/peergos/protocol/bitswap/Bitswap.java | 2 +- src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index 42acdbea..f83ee3da 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -81,10 +81,10 @@ public Set getBroadcastAudience() { public void sendWants(Host us, Set peers) { Set wants = engine.getWants(); - LOG.info("Broadcast wants: " + wants.size()); Map haves = engine.getHaves(); // broadcast to all connected bitswap peers if none are supplied Set audience = peers.isEmpty() ? getBroadcastAudience() : peers; + LOG.info("Send wants: " + wants.size() + " to " + audience); List wantsProto = wants.stream() .map(want -> MessageOuterClass.Message.Wantlist.Entry.newBuilder() .setWantType(audience.size() <= 2 || haves.containsKey(want) ? diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 466d3495..142f9996 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -194,7 +194,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { waiter.complete(new HashedBlock(c, data)); localWants.remove(w); } else - LOG.info("Received block we don't want: " + c + " from " + sourcePeerId.bareMultihash()); + LOG.info("Received block we don't want: " + c.toBase58() + " from " + sourcePeerId.bareMultihash()); } } catch (IOException e) { e.printStackTrace(); From db85f25bb5ea26b1a530f09e74c6015dc5f4f558 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 18 Oct 2023 22:33:33 +0100 Subject: [PATCH 073/218] Fix log statement --- src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 142f9996..2f87e57d 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -194,7 +194,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { waiter.complete(new HashedBlock(c, data)); localWants.remove(w); } else - LOG.info("Received block we don't want: " + c.toBase58() + " from " + sourcePeerId.bareMultihash()); + LOG.info("Received block we don't want: z" + c.toBase58() + " from " + sourcePeerId.bareMultihash()); } } catch (IOException e) { e.printStackTrace(); From 95d05bc0c040c9bd797129cfdcb50cc5793d1c43 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 18 Oct 2023 22:46:57 +0100 Subject: [PATCH 074/218] Don't resend recently sent blocks to the same peer --- .../java/org/peergos/protocol/bitswap/BitswapEngine.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 2f87e57d..22ab277b 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -26,6 +26,7 @@ public class BitswapEngine { private final ConcurrentHashMap persistBlocks = new ConcurrentHashMap<>(); private final ConcurrentHashMap blockHaves = new ConcurrentHashMap<>(); private final Map deniedWants = Collections.synchronizedMap(new LRUCache<>(10_000)); + private final Map> recentBlocksSent = Collections.synchronizedMap(new LRUCache<>(100)); private final Set connections = new HashSet<>(); private final BlockRequestAuthoriser authoriser; private AddressBook addressBook; @@ -96,6 +97,11 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { int messageSize = 0; Multihash peerM = Multihash.deserialize(source.remotePeerId().getBytes()); Cid sourcePeerId = new Cid(1, Cid.Codec.Libp2pKey, peerM.getType(), peerM.getHash()); + Map recent = recentBlocksSent.get(source.remotePeerId()); + if (recent == null) { + recent = Collections.synchronizedMap(new LRUCache<>(1_000)); + recentBlocksSent.put(source.remotePeerId(), recent); + } if (msg.hasWantlist()) { for (MessageOuterClass.Message.Wantlist.Entry e : msg.getWantlist().getEntriesList()) { Cid c = Cid.cast(e.getBlock().toByteArray()); @@ -115,6 +121,8 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { messageSize += presence.getSerializedSize(); continue; } + if (recent.containsKey(w)) + continue; // don't re-send this block as we recently sent it to this peer Optional block = store.get(c).join(); if (block.isPresent() && authoriser.allowRead(c, block.get(), sourcePeerId, auth.orElse("")).join()) { MessageOuterClass.Message.Block blockP = MessageOuterClass.Message.Block.newBuilder() @@ -132,6 +140,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { } messageSize += blockSize; blocks.add(blockP); + recent.put(w, true); } else if (sendDontHave) { if (block.isPresent()) { deniedWants.put(w, true); From 758495a4d8bf300dd6021c7cfe595cd23de7cb1e Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 19 Oct 2023 11:25:39 +0100 Subject: [PATCH 075/218] Don't resend recently sent wants to the same peer --- .../org/peergos/protocol/bitswap/Bitswap.java | 8 +------ .../protocol/bitswap/BitswapEngine.java | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index f83ee3da..15dfc762 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -64,12 +64,6 @@ public List> get(List wants, results.add(res); } sendWants(us, peers); - ForkJoinPool.commonPool().execute(() -> { - while (engine.hasWants()) { - try {Thread.sleep(5_000);} catch (InterruptedException e) {} - sendWants(us, peers); - } - }); return results; } @@ -80,7 +74,7 @@ public Set getBroadcastAudience() { } public void sendWants(Host us, Set peers) { - Set wants = engine.getWants(); + Set wants = engine.getWants(peers); Map haves = engine.getHaves(); // broadcast to all connected bitswap peers if none are supplied Set audience = peers.isEmpty() ? getBroadcastAudience() : peers; diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 22ab277b..f37d43db 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -17,6 +17,7 @@ import java.util.concurrent.*; import java.util.function.*; import java.util.logging.*; +import java.util.stream.*; public class BitswapEngine { private static final Logger LOG = Logging.LOG(); @@ -27,6 +28,7 @@ public class BitswapEngine { private final ConcurrentHashMap blockHaves = new ConcurrentHashMap<>(); private final Map deniedWants = Collections.synchronizedMap(new LRUCache<>(10_000)); private final Map> recentBlocksSent = Collections.synchronizedMap(new LRUCache<>(100)); + private final Map> recentWantsSent = Collections.synchronizedMap(new org.peergos.util.LRUCache<>(100)); private final Set connections = new HashSet<>(); private final BlockRequestAuthoriser authoriser; private AddressBook addressBook; @@ -67,7 +69,26 @@ public Set getConnected() { return connected; } - public Set getWants() { + private Map recentSentWants(PeerId peer) { + Map recent = recentWantsSent.get(peer); + if (recent == null) { + recent = Collections.synchronizedMap(new LRUCache<>(1000)); + recentWantsSent.put(peer, recent); + } + return recent; + } + + public Set getWants(Set peers) { + if (peers.size() == 1) { + PeerId peer = peers.stream().findFirst().get(); + Map recent = recentSentWants(peer); + + Set res = localWants.keySet().stream() + .filter(w -> !recent.containsKey(w)) + .collect(Collectors.toSet()); + res.forEach(w -> recent.put(w, true)); + return res; + } return localWants.keySet(); } From 4a4ecf55e0d1f5697c3a307f265ab6fe43f7acd6 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 19 Oct 2023 20:57:08 +0100 Subject: [PATCH 076/218] Separate kubo pin command out into different build step --- .github/workflows/maven.yml | 2 ++ install-run-ipfs.sh | 1 - kubo-pin.sh | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100755 kubo-pin.sh diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 96c7d573..1a47f245 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -17,6 +17,8 @@ jobs: java-version: 11 - name: Install and run ipfs run: ./install-run-ipfs.sh + - name: Pin data in kubo + run: ./kubo-pin.sh - name: Build and Package run: mvn package -Dmaven.test.skip=true - name: Run tests diff --git a/install-run-ipfs.sh b/install-run-ipfs.sh index 25af683a..dfaa3286 100755 --- a/install-run-ipfs.sh +++ b/install-run-ipfs.sh @@ -5,4 +5,3 @@ export PATH=$PATH:$PWD/kubo/ ipfs init ipfs daemon --routing=dhtserver & sleep 10s -time ipfs pin add zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg diff --git a/kubo-pin.sh b/kubo-pin.sh new file mode 100755 index 00000000..05d8e108 --- /dev/null +++ b/kubo-pin.sh @@ -0,0 +1,2 @@ +#! /bin/sh +time ipfs pin add zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg From 7dcb7707b356c4f0a660be082c35b17fccc5910e Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 19 Oct 2023 20:59:56 +0100 Subject: [PATCH 077/218] fix kubo path --- kubo-pin.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/kubo-pin.sh b/kubo-pin.sh index 05d8e108..6ca2eb93 100755 --- a/kubo-pin.sh +++ b/kubo-pin.sh @@ -1,2 +1,3 @@ #! /bin/sh +export PATH=$PATH:$PWD/kubo/ time ipfs pin add zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg From 1aed084c9ba937044bacccc248cf68eaa365906a Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 19 Oct 2023 21:13:41 +0100 Subject: [PATCH 078/218] Remove kademlia log spam from quic only nodes --- src/main/java/org/peergos/protocol/dht/Kademlia.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index c0579d95..7cfca9c7 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -90,7 +90,7 @@ private boolean connectTo(Host us, PeerAddresses peer) { return true; } catch (Exception e) { if (e.getCause() instanceof NothingToCompleteException || e.getCause() instanceof NonCompleteException) - LOG.info("Couldn't connect to " + peer.peerId); + LOG.fine("Couldn't connect to " + peer.peerId); else e.printStackTrace(); return false; From 59cc0617c4a1c368bf0c4edbf63c1fe8595bb3a0 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 19 Oct 2023 21:48:17 +0100 Subject: [PATCH 079/218] Dial provider before kubo pin --- kubo-pin.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/kubo-pin.sh b/kubo-pin.sh index 6ca2eb93..e902aa87 100755 --- a/kubo-pin.sh +++ b/kubo-pin.sh @@ -1,3 +1,4 @@ #! /bin/sh export PATH=$PATH:$PWD/kubo/ +ipfs swarm connect /ip4/172.104.157.121/tcp/4001/p2p/QmVdFZgHnEgcedCS2G2ZNiEN59LuVrnRm7z3yXtEBv2XiF time ipfs pin add zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg From 6d638c40693bde04c125832a3d2112a847e5e8fc Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 20 Oct 2023 09:20:23 +0100 Subject: [PATCH 080/218] Update S3 CI install --- .github/actions/local-s3/action.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/local-s3/action.yml b/.github/actions/local-s3/action.yml index 5192240c..cc3753bb 100644 --- a/.github/actions/local-s3/action.yml +++ b/.github/actions/local-s3/action.yml @@ -13,20 +13,20 @@ runs: echo 'MINIO_ROOT_USER=test' >> $GITHUB_ENV echo 'MINIO_ROOT_PASSWORD=testdslocal' >> $GITHUB_ENV if [[ '${{ runner.os }}' == 'Linux' ]]; then - echo '::set-output name=release::linux-amd64' - echo '::set-output name=home::/usr/local/bin' + echo 'release=linux-amd64' >> $GITHUB_OUTPUT + echo 'home=/usr/local/bin' >> $GITHUB_OUTPUT elif [[ '${{ runner.os }}' == 'Windows' ]]; then - echo '::set-output name=release::windows-amd64' - echo "::set-output name=home::/usr/bin" + echo 'release=windows-amd64' >> $GITHUB_OUTPUT + echo "home=/usr/bin" >> $GITHUB_OUTPUT elif [[ '${{ runner.os }}' == 'macOS' ]]; then - echo '::set-output name=release::darwin-amd64' - echo '::set-output name=home::/usr/local/bin' + echo 'release=darwin-amd64' >> $GITHUB_OUTPUT + echo 'home=/usr/local/bin' >> $GITHUB_OUTPUT fi - name: Download minio shell: bash run: | - curl --retry 5 --no-progress-meter --output '${{ steps.minio.outputs.home }}/minio' 'https://dl.min.io/server/minio/release/${{ steps.minio.outputs.release }}/archive/minio.RELEASE.2021-07-15T22-27-34Z' - curl --retry 5 --no-progress-meter --output '${{ steps.minio.outputs.home }}/mc' 'https://dl.min.io/client/mc/release/${{ steps.minio.outputs.release }}/archive/mc.RELEASE.2021-07-27T06-46-19Z' + curl --retry 5 --no-progress-meter --output '${{ steps.minio.outputs.home }}/minio' 'https://dl.min.io/server/minio/release/${{ steps.minio.outputs.release }}/archive/minio.RELEASE.2023-10-07T15-07-38Z' + curl --retry 5 --no-progress-meter --output '${{ steps.minio.outputs.home }}/mc' 'https://dl.min.io/client/mc/release/${{ steps.minio.outputs.release }}/archive/mc.RELEASE.2023-10-04T06-52-56Z' chmod +x '${{ steps.minio.outputs.home }}/minio' chmod +x '${{ steps.minio.outputs.home }}/mc' - name: Start minio From 9ccfa59891edc00fa24eefafaa4bab6025cf34e5 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 20 Oct 2023 09:56:00 +0100 Subject: [PATCH 081/218] Implement a download manager per peer set for bitswap --- .../org/peergos/protocol/bitswap/Bitswap.java | 36 +++++++++++++++++++ .../protocol/bitswap/BitswapEngine.java | 14 ++++---- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index 15dfc762..39fcf5de 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -12,6 +12,7 @@ import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.*; import java.util.function.*; import java.util.logging.*; import java.util.stream.*; @@ -22,6 +23,7 @@ public class Bitswap extends StrictProtocolBinding implements private final BitswapEngine engine; private final LRUCache connected = new LRUCache<>(100); + private final LRUCache, DownloadManager> downloads = new LRUCache<>(100); private AddressBook addrs; public Bitswap(BitswapEngine engine) { @@ -64,6 +66,8 @@ public List> get(List wants, results.add(res); } sendWants(us, peers); + DownloadManager manager = downloads.getOrDefault(peers, new DownloadManager(us, peers)); + manager.ensureRunning(); return results; } @@ -73,8 +77,40 @@ public Set getBroadcastAudience() { return res; } + private class DownloadManager { + private final Host us; + private final Set peers; + private final AtomicBoolean running = new AtomicBoolean(false); + + public DownloadManager(Host us, Set peers) { + this.us = us; + this.peers = peers; + } + + public void ensureRunning() { + if (! running.get()) + new Thread(() -> run()).start(); + } + + public void run() { + running.set(true); + while (true) { + try {Thread.sleep(5_000);} catch (InterruptedException e) {} + Set wants = engine.getWants(peers); + if (wants.isEmpty()) + break; + sendWants(us, wants, peers); + } + running.set(false); + } + } + public void sendWants(Host us, Set peers) { Set wants = engine.getWants(peers); + sendWants(us, wants, peers); + } + + public void sendWants(Host us, Set wants, Set peers) { Map haves = engine.getHaves(); // broadcast to all connected bitswap peers if none are supplied Set audience = peers.isEmpty() ? getBroadcastAudience() : peers; diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index f37d43db..1c430e28 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -28,7 +28,7 @@ public class BitswapEngine { private final ConcurrentHashMap blockHaves = new ConcurrentHashMap<>(); private final Map deniedWants = Collections.synchronizedMap(new LRUCache<>(10_000)); private final Map> recentBlocksSent = Collections.synchronizedMap(new LRUCache<>(100)); - private final Map> recentWantsSent = Collections.synchronizedMap(new org.peergos.util.LRUCache<>(100)); + private final Map> recentWantsSent = Collections.synchronizedMap(new org.peergos.util.LRUCache<>(100)); private final Set connections = new HashSet<>(); private final BlockRequestAuthoriser authoriser; private AddressBook addressBook; @@ -69,8 +69,8 @@ public Set getConnected() { return connected; } - private Map recentSentWants(PeerId peer) { - Map recent = recentWantsSent.get(peer); + private Map recentSentWants(PeerId peer) { + Map recent = recentWantsSent.get(peer); if (recent == null) { recent = Collections.synchronizedMap(new LRUCache<>(1000)); recentWantsSent.put(peer, recent); @@ -81,12 +81,14 @@ private Map recentSentWants(PeerId peer) { public Set getWants(Set peers) { if (peers.size() == 1) { PeerId peer = peers.stream().findFirst().get(); - Map recent = recentSentWants(peer); + Map recent = recentSentWants(peer); + long now = System.currentTimeMillis(); + long minResendWait = 5_000; Set res = localWants.keySet().stream() - .filter(w -> !recent.containsKey(w)) + .filter(w -> !recent.containsKey(w) || recent.get(w) < now - minResendWait) .collect(Collectors.toSet()); - res.forEach(w -> recent.put(w, true)); + res.forEach(w -> recent.put(w, now)); return res; } return localWants.keySet(); From 65490c600544bac4f4decbfc84284decb33d0048 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 20 Oct 2023 11:26:59 +0100 Subject: [PATCH 082/218] pass through Cid Codec to buildCid method --- .../metadatadb/CachingBlockMetadataStore.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java index 2e223877..25ced53d 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java @@ -42,23 +42,23 @@ public CompletableFuture getBlockMetadata(Cid block) { }); } - private void cacheBlockMetadata(byte[] block, boolean isRaw) { - Cid cid = hashToCid(block, isRaw); + private void cacheBlockMetadata(byte[] block, Cid.Codec codec) { + Cid cid = hashToCid(block, codec); metadata.put(cid, block); } - private Cid hashToCid(byte[] input, boolean isRaw) { - return buildCid(Hash.sha256(input), isRaw); + private Cid hashToCid(byte[] input, Cid.Codec codec) { + return buildCid(Hash.sha256(input), codec); } - private Cid buildCid(byte[] sha256, boolean isRaw) { - return Cid.buildCidV1(isRaw ? Cid.Codec.Raw : Cid.Codec.DagCbor, Multihash.Type.sha2_256, sha256); + private Cid buildCid(byte[] sha256, Cid.Codec codec) { + return Cid.buildCidV1(codec, Multihash.Type.sha2_256, sha256); } @Override public CompletableFuture> get(Cid hash) { return target.get(hash).thenApply(bopt -> { - bopt.ifPresent(b -> cacheBlockMetadata(b, hash.codec == Cid.Codec.Raw)); + bopt.ifPresent(b -> cacheBlockMetadata(b, hash.codec)); return bopt; }); } From da03d077a6080a61915ef31b70fbf9ababc74cb6 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Tue, 24 Oct 2023 09:49:28 +0100 Subject: [PATCH 083/218] Add prometheus metrics to S3 blockstore --- .../peergos/blockstore/s3/S3Blockstore.java | 74 +++++++++++++++++-- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index bb2b400b..a8ff95e0 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -2,6 +2,8 @@ import io.ipfs.cid.Cid; import io.ipfs.multihash.Multihash; +import io.prometheus.client.Counter; +import io.prometheus.client.Histogram; import org.peergos.Hash; import org.peergos.blockstore.Blockstore; import org.peergos.blockstore.RateLimitException; @@ -32,6 +34,51 @@ public class S3Blockstore implements Blockstore { private static final Logger LOG = Logging.LOG(); + private static final Histogram readTimerLog = Histogram.build() + .labelNames("filesize") + .name("block_read_seconds") + .help("Time to read a block from immutable storage") + .exponentialBuckets(0.01, 2, 16) + .register(); + private static final Histogram writeTimerLog = Histogram.build() + .labelNames("filesize") + .name("s3_block_write_seconds") + .help("Time to write a block to immutable storage") + .exponentialBuckets(0.01, 2, 16) + .register(); + private static final Counter blockHeads = Counter.build() + .name("s3_block_heads") + .help("Number of block heads to S3") + .register(); + private static final Counter blockGets = Counter.build() + .name("s3_block_gets") + .help("Number of block gets to S3") + .register(); + private static final Counter failedBlockGets = Counter.build() + .name("s3_block_get_failures") + .help("Number of failed block gets to S3") + .register(); + private static final Counter blockPuts = Counter.build() + .name("s3_block_puts") + .help("Number of block puts to S3") + .register(); + private static final Histogram blockPutBytes = Histogram.build() + .labelNames("size") + .name("s3_block_put_bytes") + .help("Number of bytes written to S3") + .exponentialBuckets(0.01, 2, 16) + .register(); + + private static final Counter getRateLimited = Counter.build() + .name("s3_get_rate_limited") + .help("Number of times we get a http 429 rate limit response during a block get") + .register(); + + private static final Counter rateLimited = Counter.build() + .name("s3_rate_limited") + .help("Number of times we get a http 429 rate limit response") + .register(); + private final String region; private final String bucket; private final String regionEndpoint; @@ -245,6 +292,7 @@ private CompletableFuture> getSizeWithoutRetry(Cid cid) { PresignedUrl headUrl = S3Request.preSignHead(folder + hashToKey(cid), Optional.of(60), S3AdminRequests.asAwsDate(ZonedDateTime.now()), host, region, accessKeyId, secretKey, useHttps, hasher).join(); Map> headRes = HttpUtil.head(headUrl.base, headUrl.fields); + blockHeads.inc(); long size = Long.parseLong(headRes.get("Content-Length").get(0)); return Futures.of(Optional.of((int)size)); } catch (FileNotFoundException f) { @@ -252,8 +300,9 @@ private CompletableFuture> getSizeWithoutRetry(Cid cid) { return Futures.of(Optional.empty()); } catch (IOException e) { String msg = e.getMessage(); - boolean rateLimited = msg.startsWith("SlowDown"); - if (rateLimited) { + boolean rateLimitedResult = msg.startsWith("SlowDown"); + if (rateLimitedResult) { + rateLimited.inc(); throw new RateLimitException(); } boolean notFound = msg.startsWith("NoSuchKey"); @@ -277,8 +326,10 @@ private CompletableFuture> getWithoutRetry(Cid cid) { Optional> range = Optional.empty(); PresignedUrl getUrl = S3Request.preSignGet(path, Optional.of(600), range, S3AdminRequests.asAwsDate(ZonedDateTime.now()), host, region, accessKeyId, secretKey, useHttps, hasher).join(); + Histogram.Timer readTimer = readTimerLog.labels("read").startTimer(); try { byte[] block = HttpUtil.get(getUrl.base, getUrl.fields); + blockGets.inc(); blockMetadata.put(cid, block); return Futures.of(Optional.of(block)); } catch (SocketTimeoutException | SSLException e) { @@ -286,8 +337,10 @@ private CompletableFuture> getWithoutRetry(Cid cid) { throw new RateLimitException(); } catch (IOException e) { String msg = e.getMessage(); - boolean rateLimited = msg.startsWith("SlowDown"); - if (rateLimited) { + boolean rateLimitedResult = msg.startsWith("SlowDown"); + if (rateLimitedResult) { + getRateLimited.inc(); + rateLimited.inc(); throw new RateLimitException(); } boolean notFound = msg.startsWith("NoSuchKey"); @@ -295,7 +348,10 @@ private CompletableFuture> getWithoutRetry(Cid cid) { LOG.warning("S3 error reading " + path); LOG.log(Level.WARNING, msg, e); } + failedBlockGets.inc(); throw new RuntimeException(e.getMessage(), e); + } finally { + readTimer.observeDuration(); } } @@ -304,6 +360,7 @@ public CompletableFuture put(byte[] block, Cid.Codec codec) { return getWithBackoff(() -> putWithoutRetry(block, codec)); } public CompletableFuture putWithoutRetry(byte[] block, Cid.Codec codec) { + Histogram.Timer writeTimer = writeTimerLog.labels("write").startTimer(); byte[] hash = Hash.sha256(block); Cid cid = new Cid(1, codec, Multihash.Type.sha2_256, hash); String key = hashToKey(cid); @@ -316,15 +373,20 @@ public CompletableFuture putWithoutRetry(byte[] block, Cid.Codec codec) { S3AdminRequests.asAwsDate(ZonedDateTime.now()), host, extraHeaders, region, accessKeyId, secretKey, useHttps, hasher).join(); HttpUtil.put(putUrl.base, putUrl.fields, block); blockMetadata.put(cid, block); + blockPuts.inc(); + blockPutBytes.labels("size").observe(block.length); return CompletableFuture.completedFuture(cid); } catch (IOException e) { String msg = e.getMessage(); - boolean rateLimited = msg.startsWith("SlowDown"); - if (rateLimited) { + boolean rateLimitedResult = msg.startsWith("SlowDown"); + if (rateLimitedResult) { + rateLimited.inc(); throw new RateLimitException(); } LOG.log(Level.SEVERE, e.getMessage(), e); throw new RuntimeException(e.getMessage(), e); + } finally { + writeTimer.observeDuration(); } } From 8e96b227eb7c25fd99c420b862b965d34a0597a4 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 24 Oct 2023 17:12:54 +0100 Subject: [PATCH 084/218] Make sure we block repeated invalid auth attempts early --- src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 1c430e28..adc8e04f 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -175,6 +175,9 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { .build(); presences.add(presence); messageSize += presence.getSerializedSize(); + } else if (block.isPresent()) { + deniedWants.put(w, true); + LOG.info("Rejecting repeated invalid auth for block " + c + " from " + sourcePeerId.bareMultihash()); } } else { boolean hasBlock = store.has(c).join(); From fb4dcb5305d8b29769af79155d033c442fff654a Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 24 Oct 2023 21:07:04 +0100 Subject: [PATCH 085/218] Expire old wants after 5 minutes --- .../protocol/bitswap/BitswapEngine.java | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index adc8e04f..d0e628b4 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -23,7 +23,7 @@ public class BitswapEngine { private static final Logger LOG = Logging.LOG(); private final Blockstore store; - private final ConcurrentHashMap> localWants = new ConcurrentHashMap<>(); + private final ConcurrentHashMap localWants = new ConcurrentHashMap<>(); private final ConcurrentHashMap persistBlocks = new ConcurrentHashMap<>(); private final ConcurrentHashMap blockHaves = new ConcurrentHashMap<>(); private final Map deniedWants = Collections.synchronizedMap(new LRUCache<>(10_000)); @@ -47,14 +47,14 @@ public synchronized void addConnection(PeerId peer, Multiaddr addr) { } public CompletableFuture getWant(Want w, boolean addToBlockstore) { - CompletableFuture existing = localWants.get(w); + WantResult existing = localWants.get(w); if (existing != null) - return existing; - CompletableFuture res = new CompletableFuture<>(); + return existing.result; if (addToBlockstore) persistBlocks.put(w, true); + WantResult res = new WantResult(System.currentTimeMillis()); localWants.put(w, res); - return res; + return res.result; } public boolean hasWants() { @@ -69,6 +69,16 @@ public Set getConnected() { return connected; } + private static final class WantResult { + public final CompletableFuture result = new CompletableFuture(); + public final long creationTime; + + public WantResult(long creationTime) { + this.creationTime = creationTime; + } + } + + private Map recentSentWants(PeerId peer) { Map recent = recentWantsSent.get(peer); if (recent == null) { @@ -85,8 +95,10 @@ public Set getWants(Set peers) { long now = System.currentTimeMillis(); long minResendWait = 5_000; - Set res = localWants.keySet().stream() - .filter(w -> !recent.containsKey(w) || recent.get(w) < now - minResendWait) + Set res = localWants.entrySet().stream() + .filter(e -> e.getValue().creationTime > now - 5*60*1000) + .map(e -> e.getKey()) + .filter(w -> recent.get(w) < now - minResendWait) .collect(Collectors.toSet()); res.forEach(w -> recent.put(w, now)); return res; @@ -220,13 +232,13 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { byte[] hash = Hash.sha256(data); Cid c = new Cid(version, codec, type, hash); Want w = new Want(c, auth); - CompletableFuture waiter = localWants.get(w); + WantResult waiter = localWants.get(w); if (waiter != null) { if (persistBlocks.containsKey(w)) { store.put(data, codec); persistBlocks.remove(w); } - waiter.complete(new HashedBlock(c, data)); + waiter.result.complete(new HashedBlock(c, data)); localWants.remove(w); } else LOG.info("Received block we don't want: z" + c.toBase58() + " from " + sourcePeerId.bareMultihash()); From 068a00130daaeddd650911ff5b1f183c27817250 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 24 Oct 2023 21:50:00 +0100 Subject: [PATCH 086/218] Revert break to bitswap want generation --- src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index d0e628b4..6deacb51 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -98,7 +98,7 @@ public Set getWants(Set peers) { Set res = localWants.entrySet().stream() .filter(e -> e.getValue().creationTime > now - 5*60*1000) .map(e -> e.getKey()) - .filter(w -> recent.get(w) < now - minResendWait) + .filter(w -> ! recent.containsKey(w) || recent.get(w) < now - minResendWait) .collect(Collectors.toSet()); res.forEach(w -> recent.put(w, now)); return res; From 34570b8785769dff810bfe54e696199b323052b0 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 25 Oct 2023 09:46:11 +0100 Subject: [PATCH 087/218] Commit blocks for bitswap mirror test and directly add to kubo --- .github/workflows/maven.yml | 2 -- blocks.bin | Bin 0 -> 2191681 bytes .../protocol/bitswap/BitswapEngine.java | 3 +- .../java/org/peergos/BitswapMirrorTest.java | 32 ++++++++++++++++-- 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 blocks.bin diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 1a47f245..96c7d573 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -17,8 +17,6 @@ jobs: java-version: 11 - name: Install and run ipfs run: ./install-run-ipfs.sh - - name: Pin data in kubo - run: ./kubo-pin.sh - name: Build and Package run: mvn package -Dmaven.test.skip=true - name: Run tests diff --git a/blocks.bin b/blocks.bin new file mode 100644 index 0000000000000000000000000000000000000000..e834bbc987741a28295277e1095a4995338599ac GIT binary patch literal 2191681 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ⅈvbb6((ZcpRepPgnD9ONQFxHFMeR~kQ!gA z7o*J%T(gZnw*`~)Qu)Yry&96Ab4Sz|ERJUxz2y|a2Ue&@@y*$g^uo?l!lRmkGWnZ9mSB!Uv~mDcmVC(ocz+}Crz=R6~K zRCCJqV|-1qzTI5i)|Uwobg7z4$eO1m3xy(l$bc~Tfq8V|{-YDq8eJ%b)&H*i8dE-s z*Dc4cv4t*%FXBXK&wjq7kAF)A^`C)*V@z|eX=jll(pRVF%8j_sE=d4yipiUoqe7^?Qhn^PE-=vaOntMY_3Ot%t+wNnS0a*7`1QxHEZbJy$W6K)T9KFEeb`$HRiLDFXSv@CS$658?uh{vNQIS+oQ0 zFlXwDi)#Yc?|efIVndyO5!=Qa?a?`9kwaAs7Ka0-y{nt>D`U(fQY2v{7jyl37ONFY z>8QLv?{8#JDgR&~Q>S~Cg2EwpGkYf!BUfh-RdqfJxBPE?z6UBUyO|#fM&5;|F%Bqx z8x!A+)Ar3KSZ-ABLr(1MPrVAek*3^jC=lOSx|>w>#4j^N;V~;GCE2*x7tCK4ged0$ z_jvy&FDKwZ>>Pr3Y>56(e&~NOM28BE@(q2kc|en7?Wyv)n{lj#WQirxMN>v`HFT49SaFmKuCD#?{b_b@-` zeXh8$_v`hSojcu6xa7QWuRHJ0bvkAojEUfkUtsW>jQ47KSAx$XmaC^Z`N;oH)FRh8 zz%nYFfn%`XJp7+v{ZEYi7qC2hz3PU2yC;+>zoxI&-Ml|+uJnDlZ0VNIezgPBTgI{@ zuo7kP&ZD}lQ-@235biW}Dos5%-fK-FVVp0UOOA;|z69o-vZ_F68(hu+E#{Ba$QcV8 zf;ku8Q^pt|5iHgt*KFG^&oDZ^4`|~;yU`vYk;qC^JiKR8cBO>UlHzhpX=XS>;jEm4 zEBVZ2?E|94goF|r5-6~K?ua`0KG9=Gl68h7=fA*Ih#gJufpP!f{5{;q!Nvi4pSGxY zWMmo!+OI`COSs41aRgH{GR+Xj>$*82mE9Px_efqBn6~S_AiHbI?0>@u*GTUM^G-#p zK*t~tC!odPZ8H8>35bvZfk!Wf8aLZ8AAHKuP#ML3;)-rk?O(8Lfzs?Vl64@d=;U0s ztBW(PxucbdFk91hfWUD%E)3IZzoo*Cx1%1ituT(s9|HA%BL(4Di#hx4{K{ABy_jTu z-jA?^=@e9kG*6WHDPak9hF<(5pyrx3U9P;}%e5uIxpo7At!s_xZA^~c)k@a$?vIm| zQ-PU!s$$K|&eZ+nsg(b%)(U@s!Y5I`sUc^B*fZYL{~Ze^WZ4u>7T)p-BkWlf%`}}) zs-8*nP0H$__XWyKZVj1?Nasz>vAUhB{lw1W_bEbHq5a|D1gQT@x@xCOPa{xntv7VR z-yk)`(M@SpyaCV((;QYVxO61{~xdwROhRF>J6)>v4M{{!~O+%ChuelK!gHhH-RO z-DB%j@&sk(6=RMjbNPmh%KlK|=xMX*m;J#VFN@pyTvaTxrZV(d1Rz~?{uBd;kR9>> zMt^@c@&s+DW*^jY-bm!wbxM_>Zdiy>{_ycf#f>Di6wZaXr$=BRx!YAj zevka#@6X=-?rt-m);c2bhD~dr$kr)cWo2&%bLL<_s+8dZiLJBsh7VR3Ggjf`BqiG} zYEH1&x#CtjZcti9eO<+%P&|1;kc{W;H2t%s1T=m*#fzMZ50mdd(>HB;c1vk6$m?Yf zlbSdrSLcqXgKtPzf%pQ%02uu}Oycf#+vr(i;Y>0p&f2^7lC>mDPnG;5-?|HmAyOH< zdk+?en-Etp+6`3tDp=pQt2h~BJR{`)<)6NhdK>TDQ?Adi?K41p;j~$5<^*%Kh8tUd z`j?*ZErvkc7;U>3%SgTg_@5kTiBZkcyrT!^SBBYMF|B=OwpOU$LgpCn!M*(OW;FGU zAG8>bskD{n&tp zXPB*(_2H2R{F^f(6wv$%A+a*QHHJjQf7YBJ%W}HCKO39$yD(KQjCa&UOyeoP_uh52 zf;|OYw58CTq4cDFQF#oJp7XDh^t|8QFNY99g_8y!hvywc9gO~7hIFnbD{~f{XV0mNYA{^R|V(CE~XynNt&{# zIR377ooM;QhMC5u47R_9mu~A)X(v5+(sjL^bBVXw*J?F<{;TQ^!V~LP&^<)#|6t(+ zFz`y)zuE0_EEmSt3<$pM$Ue%Wd4O$l<>#%t3ioYqThp80eOs&r)&#)O!wcfTf7VZ$Rb)JNMN zzwFZuBi-9QI@s7MiGhkE&$)bw9}UD4KOKX-TO?;A@ZZC236Tb)zu%TWA2tlk>XGMP zLBQryG31?g{twI z^{4UQW;CTj&Q|F^v|#=bt){nxel#Zgi!yCrU*#u6VzGrxbyR1FH263M=pVcNl3T<=8)zDSu48#Cm$O@6X9)#W6C_nOMD?+BpqJb$GICt*6u;1>%MAHF6V^1MNA z4A9S6$yIPku<{N9^G=0ioMATLP9G#c@IdxiJM($Nj*jGp@rMXtab0}$;) zhz`kv(cjCEnB#JZ(^rAY7hRb=NLT%S8BUb-DkZ#r6pblp})65c?zle>7ZnJr~7X(emsx9;@XFl{fVWCA>3A8#*fTPGKGCvc6^@;D#^`hT5HZvA?5 z&9se+!1z|P4kPAyC%GH>eZ7+v2bw;QNpJqVAReO??Z7ZDSSmU61lep}5!Ct?cE^JbDD&|8y4> z%sb@+9^Yj>R8OF){fz1d~3(6nDU?{!zkS)-`m)gci|Y6QE9WDiT!oDuNoERnU61=>;N8x!UZ@_^aAx`dPn+C5f?`a z(PBL;RR~}DA>8BmszU`ElFdz7dcu?XT~R3GyLL;hv*1$D`aX5IzZmpIcoJcNU-NT6#(z0$VLc2i4&RQ7_b=pokJNio zK6D&*3Su)+w!umq`!eNz$P5wmndmZ8-mhU*VCcU}kckjvjk$M&(_-Z=l#CTi$GoA}$*8 zt}u?tg8>B8{|%|a8fCodSU%a7xKJyx=RI+|BEfXRUMzi$N~-4Wp0^NWpumCh>}66G z-`J6Mu9}lW{Lzsu?fyQudedv^Z7}=8E(4zLVBV>-&sKn6KmY;>{4PPR^o*`wz|?2f z?iykTqK~8$kvI4Y#gj%Q(~w!NJ#KQ-Q!n6HPe3ZX)fgFCkzKO$`G*K2GOmliNKV>s z;}P%A7^pBdLdf+)g_Ax6=#V-X{k+mO0K7-_a{M4Z2= z`LJ;jEDi?<=Ysj`!R7g>RPv5`?yPG&8}kd_>AS7s#xd2C>N5qQCqYhSZcN=^cFqPi ze|*2AhB;?*kV6Hu-wHjQrG=Shu{3MPIw~LvZwrXLf0F#pyg`!3#FPTijPPcqD>NFcx3RjCR%vyiJBe379AJq0#@Po;}n4$w(M^^UHUfUt#WK8}YS2 z!nwr$@gDwcIVMY)=TG?~sN~nYoCQwXge{3Lt_?{ECI%n>US>#f+3cgl^0eN<0yqfOOJe|Og(!;5r_*xhI z=r`thE9tfz;_({Pq=zNzlc#eZu2Z!OL{{RGR6gYJ-`g>X8E~_o=Syp%L z`<23j&9rT5>cGt1;;o9?xixO7#xnol2%vLyjSjd~hG-m$sA+diHg1NPHf**;E$@=W zB@vUNd4>ZX7>Ds=)NN_ZY72}$RwJOxSwU9@n6b@K4hIx>obF5`coEZ?a#R%4 zgGd8?MLb1{czv;>a_VbLB~f)+tfQ2qRw`#)sq*?bUOEADfzd#w&z+>5Q3VuUqvi#s zQZMYHSnaItm!&&P`ThaZVWn>k!*Ps_zo@YyE=ea8A!W#aaEI~{|DQY^qY2fV965)_ z_QU_g+kdeirs8?Cf5f0~t99#lW-_3^<`N*m2@9#1^%KXIx}Hn&xf51yzu z;eIX}K;dxKAH6eE@@8&2csX?|n{V{8!&zfh-3${h)283d+itix$z1=ET~h zcobl9xEhn)%dH3+bAnCuvQ(ngT@|V&{cQG$GcWGBZ?^p1fJ>hN59mSS3Sz;*vR0O6 z;9+w1lU@-@H(%eRf835#_j`xpiRh0pl;E%TnSX0%r&!kxCQIIastA%)UY@St;)cD` zNiUC`V8`O?=(mY^YbO$nl;xyeV9-*J#d(CP_IYVc`Ap;m${X98n3~&Ix)`6_sPI5m z_@?yvr|@$fSl6v*1p+vDv^_Dx&UMvS&~K8Xg@%7E*Y=+BvgFV0$)I4MoS9m~C^ak#E+QmY*8?(~Bm-I56+j z7r-@W7OML7{cIy01f*j+*CyWEwKtCi7J|X+zkUkjwy<83ZMd$6r zTKC~7-MzdSap@@`mfVhWnf6qPbo~BqzSnVBltO?vig$;s_m%uE_OnjrvG3UILf&aJ z*movxtqbH2TL7OPF-Y89EHqHY0p5l6_}8E}Eq%SjI49qgKVAuERZ{6yN|@S!Lm{;w ztn^~%T|(qQNgG#_fZ(c5IAF>Dv!xIOTfL{{6y6=ODdLI+7C-&8#k!k(FDS5~xuo&0l5s7;BnKw4&U zE|j}J-}}X6n#MXE!t@5W3*xKQ>WeP)T0m?q>_I-=28jIeUn;L6+qOoH5*y7J*1$|# zrWiN8J3P=Su!NiTFei5|j5S!w&D6y5c=|{zo3l;`cK^ADRYZ&Jq0NaZ3vvRV7*`C~MTCr?C7e9{ERfBUgL_A~{RX_U1I_7sh3chW+?_%FE zJVI~4?CkAaTwIRnjISb z%X9o!LiFq1GHEwh)X5A>3x#u@N*ibAzMv~4alWvWiaV6-s{pezaRS_#z&K8BgXzdO z3A$WULPsk^|1SH(cV1z7n&UJH94(U20VOky;ccd9tn+#UX6$DtK<#=;JNgl6fS@aG@c3SLFOL+El7nR1XS zEdO*)+AuvVmEF`Q)i3B8Wdc{ZlEFG+qYmcgGS|wMx^z%*+AlrwudHtH^c4z3`OjWD zBK}W%DOT~}_M_cxqn975<1)M)3}_nkW(-rVv&0B3Q(h!2Fgb!pTzw}&<(K@O=WkRc z#0Z$^cJAqR(I*NPf3I{T&}uO@?61cP&wg!3VT{6DoWB08k;Yb;c$3aH zWZLEUy=S!eiA8b;-}BX22HMINuhuRMUJ?1hy^VvqA~|W=fZ7k3I3-6Wu69S4;6aq- zjP4^}Ojd2oIj+go|WKUpM$Ek^Exr-l> zRj2zsvzr^a)dBVDg>N%=w;;VJ@XnRm0a2*T<(Zu>>@QoS+jhHsF*6Z*_qo1;d8b?! zW0wj?AsGf42V;%@EonHO; z*u##sS+Ppn)&-bFrzOeL)EIui1CW4~dsdC)t37@R#r@8&o^QyliRhoRmPn>z+1S#n^uv-5?n2w`NlIJyvSeTUhn!&;gGEQ3gN%Y&1A= zF#3CVAk8wtXf+~_R{Ic;dny7_~@36Z* zGP;bTToiq)G-co9h538N`L_&Cw_hcUX*~I5D#{=DS`D#G&0foKou-O`J_Ql|%{i#& z68a(WLy&+81kwK|kUSlt{KU98oxL7cZnTx|DaI7L`0A(KYF>CD{x7tfZhE-H+9f>^57U8$lVRgAQk?B>!Va!KlD ziZ!xv8=hk|ezUlE4kf*?5jpw;IxezjqaDJ|Yw!eJ@!*@e729#0Ou*P_HE!wRt_OgP#iwo&`L->;HR>*@Hw9h;<1#VDF z7e08#!qMUl=A8-x*tnX4fB!Tb1hH1opz4f@DwwC2;`N4l*TJ^te8eK-xtD4mJiiac zs$uNje9Uy|o|gT6^{b@1k$gRdo}LPy=HnV1#(0y$C~5HAl|Gak4dy~gymLp?!8f!K z4;cWXzlX_YQwcHZ{g;q?b2f`PuJNL!AASUgsgb=i>}Q|n$ZUX4F*r=EZkH3jJPA^j zrj5S7=iP(mfHWiMNP=vs?NosNV1$4i%sZ9Gvj!a>juLT}gho^6>X`bJo+@rU^a*Z~D(u zm~~lDqy+5`ZSVy_1mAl9Xb>onlkZ@n>#z*IA>IrddbEaRWy~cL|7?n?(G7t-MB+Gc z@UZtGqC|Nrk6Dl%qqIZjkL~pQ9a2V4)aIa6iXuiY=%Vc@)6&M&{G>YgtUZJLWv_?v zXdrT`@V&$o?7jp@5A8*jM4ouCQv z^iOEAvfd8+Nw`HsSWSzewHRfZ?0YG5k_%U@?|EZ5$%|mfB7pD5`)pM-Slljd@h(!2 z3YvNtHQIapYcJMa_!v^gbNP}Vn0LA!$lS=&(#+k|(%jO>^yn=;P*{1!Z5a{K)caf_b~^c z);Ms$8W)}`K7B<;q*>n4Z|xTns^nJ4u1BAi$j(W7Lsq!_7npbI8kV!sQ3*Qujh-=0 z?DtC-H;_W9E+5&dU{l~1%XTl>g+%M`0U|a-qg6Qv3*T1_5u?tH&seLmR*A-|KJQBG zdA$+5dhcn?;)CyoLtKzcgA53RAAeLA@QRNg8~VFyYI&lfII%~hKSiCs_ZRGV=x48t z;f!N&0A0}T;Yi(o

#(zzK3`(+LM$5AFAcbMcx*48*7NrjPbxwtB5lHH6~Dyis_FAeR$IzM z^&v^?M?96=9iw+P`N&}k(mY75;-E7V*#)$Pet)aX$k#^HjhG*pe}A=wY%&x;82oi? zE&ij!@A%2mvM2~|>d_}t7o8qW&SYPWXA!Fye3h1$wBNN)ANOEeF`fNIa=pEswxmsp z>yn_v%}BmC(oS)=&FPkzILq%rIn0wXaAVN-_{f^(WrhlsjuTxKR>Sfcj~;unKgpYV zPBT>*CoPv4b@MjQ)VrJ+_63f5&0s5`puSKp-H?c~2|gj}?tnx&?wVSs~*P^&;m4QBD>ju&v2WAmzJlAykWgMR5x$4dF z;g-V2NTGY3YeHnS`&)&K?FPbN9)jIy;%H$7gCq$!N5Hl|t>?j|_RBNb_CBw&t8gU# z_%)|d58L*#{rqBKC$`Vn&+5Yr`EWSsa1w9I37@rkvgp}C+#_kvMk{2J{p9KIQ6?3r zx)}{l8hk*qbVwbH{@!lH?nv34{_%3l@6V%#??0W{YnXkwtA#$yOkgukF?l`A28+Y( z#!_nExBUV-OM6@MrKVGPrKnWq2_N+KxyrhKHwEqbc7S<^$u^#Xl7kqZ8Q<{fLBdg9$qO*1EVR=Qj;J1!81KOFlJ1KcOD{s`r&Pd*H6a2Q?>V?F! zVv*f2?)|~Wkxrus7jn)pJPwG1&w~vIrw>Md57LmJ5FSA;Ylc*YFr{z%JAH1!hbxB8 zf&%5GzMnY12{(Yn;UImu(jYEU{uZ{NU^!eX|1(LYu*NkpMV=x0i$RU2Qb;-A;iR6~ z*52I08nS;u`=Z0*65t4Mp1P%2xbogu46$%+k=Vr8bSBr?s(5~HRY8Yi*B=gK?~gs7xmMt1Au8H{Qu+u{2K_eLpH$Z?;+FC zk#j2iy8CoZL9*h;!rjt7XQAAS&uhXNQ+e?w{N1jB#o>^>Y9*%_9p3)Pxm_%1v)l0N zr8L_Badh*BayrHnp7n_@U>-sy05tywFNZ=q4{SS-Xg=kA9)FQd)A!@#N*T3M4=!Dc zcHda}S=v2nusFm zHuMA9;OjuD;N-#R?;#4AGEO6i;Lbq$Rq1|hbALg`r7%R-rPbrtiu}C#)%jYmI2@ul z^Y``qvqJnI_pE6v?-WKU5j(26w>pB(;`4Dm_SaRxJcQs8kSJznyWViP94ES`K6UbBB=?6pLxxkVwce70~>l<@V+`%as{algXOpZn1`UD98JtztdE_4 zAWEkULk6Uwl!xw?)#zb0=AWI_lyV3dRr4<4Cak*iDgHIvg|?B|^se>S2K-r1vBZhO zT5hQ}`jKY%5;(@Lb>S9t)(1n00E|Q8;PY_1LG;1s?`7%CzP+Tj-b7blTbP->|43BmN1k0)zQr z9zqEv%mj2720d>Po{%}EFa=e!@n|a*jT5k6lY1JfPSDaP639{BiNTwQ8&<>f>`vIj z@yVNtuLqE)Cc4Nh^4{)Hzk&iPtfnis7xM;OW7%%-#~N2Xh5q}&6A z^_u;OyFF|qE`3&>F?0OfH-3B68sOkN{vyNb$fQHc4TeYB;G_`&`p0ggLA->L=GNqo z>hJlq;0hA7SfhKD)1k;-Z4l4D6B1IzbOex%q2i5nv#PzJ!E*yPP8|g?`|T_ynfYzqu=+cLRH01^QC0G0qcX=6F=t9LmK?{Xj7pg!3}`T@ zKE90;zN;hX{+!kFzSqy6$j1{m?wS9nL}4A`_Uay@qPRhLZE9j`57R@D5SRBI8kZ5u z@x**?WaE#~H6_|-3t2AjW`Qb$pDNSg5yfSmvu)2pJ0Q~rE}8wrO97;E7{W`7ws#&6 zPW=kqoV@w4npnvyv-!2(1Rfy({-l`B1~_8IPUbKx6HZ>f!`DauKawtC61r|yak|J= zeDiHd@2{YdT(Xg%PGvl6*8S4Xnv*Yk>)Livv(w<5X#0%~C%K|~PEQPD0@8N*@&*ze zL%(>dLK2GiA1oY8>c1yn%%0-rTE3fQ;uWWv_~6ODa?d_T)_<}dH6n_g@Z@ z`IBzv{_r=2xT&!OB?K1MG5JHF4mkj$zds!2$hDZhT%H#U{;AtoREc*pTbmNg35%js zw_qo3ImQI4c!W#Vvm8@h3jC+0XZOGOldM>g7BHlBlx^qj)1#zenyh4OfO!Zfs4L9L z#mWL6K0h$VPMKc_@UQlF!V#x<>Do^ctErSO?fU17;ST9$?3MI$-n_8kaz$i!(aPBc zC*4o=x?lGPsOFWC~p+{MMd1av-$G6j(6%9W_CMc zG77;w1WyrWabnton=jwOdM3(k8Kbjeua%VPy#o+)=bFB(df1ali2w!$neIWROrFGE$ zd1u#|k;Z*fHnWdV*9QbCGO+-@ve~=Bs+{j2Gx8{Y?f3k(Z@6ER<4fgd2hR*&SUa2C1l2CUK?*4f#QHUF z`pt9b`L9BXfrMa|kw-xq#CNnQ_46k3g2CyDsFRE>%q*Ni4DQJD0l&*rb~F#d6VRcV z#Pjs8s0%U?R&p(T$J_ssyZyxzp8ZPL({ zxb#K4GPL^#WYl9FlLv#t?FMlGM*jemb!W}lVm35RQ{IMKwyn$(YmV(k;um&0^n6lH zBYmKI_i&&vz4E?4vMUpBH~OT-@;xTY6G@OZ-I)FojtK_ z5no-xbtG_}wB^c#tNRAze#5-?!N_&3*hv-&&)^_1a0nLIdJqr)1lBo2seA^x3QpxA zv0ut!SwU<`TdArS&G+d?>~V?7F`Ws=&Ws7tIe+`vfc~sfw!&Q zI<`k+jT%^oA9Jm|P?vt`&gjLGr~2oe{H~16(w$G)QNzwLk_?_uOcd*wJQzSyb;to2 z{k>Ev&eE5CAF8K!+Ya5tlFdL0(Eaf_V-CNPOlvw4O?y8REDi^XSDFC-Y$DnlhFe=P z>(f63Wc9TMEiS#yHaf@BcAk0!WI+*aDDbKZ=r;;3B{=f&@j-HRkWWA577R-hu7W+r zPs#7&dvpW&1#O!F6*3E3m9p}{$eqmrbR%M$bUbv|Ro6cc3^Z=z?C&=(g_xQ#&o*F9 zP$fm_oiT+@9=!ix;SjJxF8&Fy!7~F9G_jky%XB}_u=@3Pu5uOW2;HA8widb~fR_!w z;R6S3CBR`3OBkC>WF0$g4=vSx!e~mDA61Xa<1G3^oC_Ui0TT$41@5?*89LgzLC;46 zwF;HEoiYFzr1to&D8t@cW0Kp8C7gO!Jm0OPYPLAP6tvs_psU7ZeZF#3}6q{{aI=?@cw#g17B;-1GzUti42palARMmk0R=Q@Jt;R{mVw zMpB6}B(;~PGj z)dUaz#Ny$Qf_Vt>EH^hkPBtJ#(7uDKh||WB1B-E8Lz%6gKU_)~WuFRW2xKyv*SrwT zgWGfM(@kW(D3a;OD+$g4-NHOrre%p&@UTrNJ~p6++~hwUL{{@d{9Z^4l37mYK zVD$Gos{Xk<*525;l(qn7dd37IzRcby8U-dEwcgI(Z-^JPLPtMbk~ZgS=>~ULhBqyE z$o%?{D|D;}IAw>T)Mj+JQ2BQ*T>f#vHXBE5T_4Fk3r_}MzJOAE@J4KV;le=kXSfd$t^uFK!sEgwcm>nz)D@9K!W~QLK4JAe)Y( zwlde7|2-pNMi*Mgy&KlgKah)tYoOlkUmK|hq~KB&E4WCmgZ+?%w9AQ6T|*fMlVq$k z-zR7{!lsEmx?80Z%tP?=+-*!?#&&K83JR)@KIKstAc^LXnURrjzrx5Z8EfbvL2mJ; z*Jlp9DZ|1MD#}0VpDQpdQpw zAc3t1(Fdcy-+E2)4V%(uCNp7?Fr^ySu;`1`EV#`1(H?wX66vtM-*E(s!$CUhNo^w` zob*0&mh|(eLctRH%a!Fn5mUasJt`?k-9J6Sypt5Xr6K4;VFP0ZMMV%!;Iun^XXIeB zU*xTCIwn%;>o|lNbKNL2T&`|6kz`K)*&1V_XflR6W-q)yrPC#prDbt*$z5uAHb= zldVru@4(`4sN6n?(&g)B8Cmf5~KUwts|7kES2$QlGT z0u7!2|HJJ*#Md)}okq94!~3pvy;>&eyUC+6c4jO7qPc3f152YUE}=$kxf7~NpYHI_ zN9T!uG<_;j_dZX<(P-N_PRsu2GPLQCoo&EB4>S{0_WN&6a9AZ-YV&i(^!b-R$ZLqg zZi=m6Bv1{d&sM>3aJ0amPCJ68)@I_*CMt$NtAj2Pey_mKWCCC7q#aX~gzLgyYo85O zz&r#6?hLbsIe`bFAsGTkF|g}S8JP>TqcYW4Dw*#dD-%dT?M!INS98WtcX=Q#B0c(9 zGqCFBa^ZH~1Zm5|rUAe1h|Aa$S>2?jq22voqUgi~%oCZRKqBT5^+T8r833cdm!R9P zDE%b0B|4O-`7Y-64vNsj5_X7a)UpT8^S&sQQ0E4V!zIWD<=3>!Gs+-p2XxEbr+x#a z5_$iRw6B1w^4a{eIz`|BM>?cS=}u`RB&9)8LZuX>L6lMfQ5pn6 zP*CKX=SZCYyWX`vUUhlzdFI|Z&))vdo;|x}53}2i2V}nPgP#itdtQQhNQ({Boq}Ua z&aB*shGJl+1M2&P*{xvAmEQyc6}7yHuLeK9xDrcuYl(P-0+p#zM$W}ZJ>NIUSY64M zMm#30;GSe4rhh&QXLn^3uS5)QD65>M8apZE(%~G_heimK0Kozn{XJCU-?a->Q1apx zibPIHkW3QsPG@fUN!|K!>TW7!DM{KscsT;9tMrzp;u9{o*VrdbvHXkm*t;dl7ET*` z!hQ4Ct0NV&!91jst|s6%B1oDLnv?T{ey=b3!r;ZQD*AHk{Ar0*CErs;feVvmABEZEJ#Bl>BS*DowAZ|g*{A%s&kD!uQ;b# z>O1Ja4a$|D-KCLNY9kQs1@nFdh+B&VflGwl?5)7z`{RB*BzK%1E&keegmvs|@}yu3 z8>xm`JvtL}riXA!5VmjYLx?kBF_ z@RtGYr@)^h0vw$-3j<3r{Rx##Qi+A|ZsEJ>-S--AINe-Sc&VtB-3M-Kva~#Ye^pI< zF4q$NPj7fL)?RAc%cL{+1zZ9=Ckh}`4%@XMtlhg zX8YmQt+SnXyVHWo0Fgp)x4;whBp+ZW%pRmsu z!~@unZgR1#kQIAa?dG$bhvQ$+iYh#xZCl5hujF2LHN=9FMTkDNn zN3kizttM*ic2@KqM_oa^(@C%@Fb_$u-v%dA5A-^)Wlnfr(KmKpbDZ64dfwnxYNy6~ zg_dEjFH>dV_7>t;r@TJWYbiB-?;tCvs_Pkb_m;^FXgd#KGwA;Zd$VlXb36y3b*#h9c8siL*UN`L)dR*JpWVH-zeG2O!%Ku@pPC z`vi7>EjvBf(fIb|E!-wtcrng&JzM&k>D=Cj+v9Fv9+JK?hdDdB!446EHoudd&JTFt zf}8o8Rw-iywr$mWW%8|{;OC-3f$SJUy_ssyS>sQS=-*KC(eItbi7bBa{|7s&5at1K@OS_cL?4X)9;DN(pNy1$rwQD}n#B+n!hXbS+!y5Syqhs1&Jvt?7kR6r zPv}h$g9#@|9*b<@4-x4d?QGi;qWO6Vs45Tph&&iTKpk)ZMt={~D?NAJmeZ4#Egb1H zm6n+sgki)y8bq&e7^DnPoGXcjxUgU zQla^=9&i`R0>r1pLIUMYK?316+zhF<|H@3^(5M-O{a2}MGLA6I#-BBFOZ$d{ts>{R zD>>Fzs+53%VCn)&H{5LC$LBf-D~IcSR8lbQo}n_CI@c_$emBom^XkYiYZ12$ zl(qt=E{+TCe}>#0oHKmkzRbL}Dl~UpCv{~7bLrmKYrMK?UYFkw5`!}ij^%JZ_Ex9(O#gJ-PW!JfLy>h5XaOGB8tctGLC{7uuMZ+eq!D7HY_R z%1`Gruh4z=imgn)bfxUotUxn${wG=JW=Zgkqc5QQSox>jjU&S@2*EYr-$;1ar?Fe^ zP^?&0>=t^O!{@3z)bHpah+cZ9pa?<|~FTul7YXFQS_kORviCL^6q<$^JxS-TH&-^Yz|9Y3O*! zFOC8$EdjT@@l7jIHANQ;0hfc@L zI9NFQJ#PqOPa6dDkTifZ%+A^k=KQC9JTRZ=`l)-LkoAGp?9|zn6JRo(Tu(h|dhI<%6E#MHA&#*;#-#B_5O9qH)#=W zG`am*9(bBO%7`y;Xt?u)%fKi-n98FLrGh9$o8>NWnQ=y!Ia2F1U{|BUh3mJayFnK{^nE3R!Z2Is+uJwpGZ2r)#b-d*d z_j4?4PJ2)TQixrpHzf2&svQNnYD6?G;l6fp(gWND*WN|`e7_=h-tfRTU>;Ji2OKv8 zrKfO2dJGY3IcW?dFkmC4ZR}HA=w@%-{&yxmABmpFaKV70I_$vL*xSQx0p=HmTpq}_ z$BdobL_<%O4Nw^!p4!sI;4I8(*Xlu^!H1T5}%(YsEXoU*eUBX zuBXtq!mWK&B5iiykGeP1A1|a|053fQ`4RP6>C0%vlj{tfK5th( z%IsKWHe4Pi}dP3A`BfL#-8Uf7vbewqTeEa854L;tnJ$7AT4+~ z0wxT~h<*GsU*FB)^%1q48;EVHR;t~w(!94Ge%`x)_GKyH0ja>q#>LXf*4EzU;1mkj zYrv&bC)7(J_S@AnuxlPzL$eFQOX;NN8)8FA(cM^M;0_7g^dmS<-RW~0FP_sH(T=y} zRY|d)VeC^1bDX}6$9Fzdt1`_Q3en>tG4Ka0XupGa_$RPJN6qS4w}YA@-;@~r$R~vt zHK~6#krZyeuUEsXPpu~gUXB1uBSorgI=k4NTcKVkuW&}dgP?wt-f8INy9y~KS}`~T zDw0fr2&b*3Bit0y;9%VWKlp?WRN%K*yc?5?Se`X5GS^eh%OjE-zv{4|Uy?ATj3LYq z;rnulN{FO|h6k5qNTr3oR^dAK-ED$bTDcDRWEa;+e-K`A9t8j^QqT=pWlEI1PGW2*8Mu!>|Wvw!$qbV@7I** z(#*w;a1&_>VFXk8RV{-b5?Q7|D{Lnx7Y9f9;l1J@J;imx)feJq*!*mw!hG3JsglU? z^**nJUROAx%4G_@A5#JU`%^3~&Nx&p3b9IOl7tHLrX4E_Wm zQ%DRPfCc%QfQNqq>x;8?$X)tRl6j43q9oM}lUkSg%{WsB$lu*2BIy|CISN_0J-MK! zlc|w6o1vK0jA=hq*0>$X%;5iH?3Le7dfRy@CW{oZ;Bt3BB**?wwv(@3P=Q+XTyrx` zg9SWMdB@jV)P{C(sWx%!tCQWOCIf=-*JWQzeAz#SaF6L_SsACOQixP6vgI?0!%{Xb zh7v;ZgbqH@0aG^#pDrC4dx-);;!IKJlx6RPfgJuj80ho z7$oUqLhsmXTuGSB#3y`*4{KUhNTpH~C-WKV*fcm#82Ynk7PSh()f*u(?G>*d+zRH4lv#5!QZW3-r z?sCD-frmit$?K+@sH{uaGTd`Uy{#1fP_=aDn~Ys{4pp;FADKK9+d%@#%-O-w(iLWU zP%;CYmXk)KgSt8;Y+_YpmZ_WHEPg5%v&qO0=RTkCiip9A>lVtq37a^-Z`2kyMZ>pQbIm|nznSED~tQ3q#x%92zoODin2{dnfx z&tU@6W)g;sGyQQ7L%PLP9(;doOC@*h>UJP6_dv`ueg+#f{9m8wp#(nR|6t-6G>DIX z0_{UPT8o)xO+!b5oT+QO?{qMoAa=vAQ!g)?b%L8qFe;m>i8VNy;;D(S0rQZY7)1Q%K%)47)pxRJv>$=l40*$YcfAVg3F4G(rA4BfffyJRQksJIm{O{z;&xFpu&$z@R_O=?*SD1(oC-@26j?x0>Qv7U+WBf?oV!bp6P=7>eV*MVz{*KIGb))??`Zzq)~mwz#3+l%5j>mx4mAosmi zVEs9isO`$4w(L46x`KT~9tn?ptp*&J^bCN2@F17@-n>*kGvB8lX1($x;EwetoGvK!X2y zBj1H0*v+3)Iy~o@>hC11r3n_Q{<5}=PV^@Yf+|LFj_E@qgnNKs0gV10Dl{$zRWS^L zVXqF!P)GbtqA1UR??s>3#L1TjU%6=Nh=P|Rpo(hT_g!-xEY2JKDxueuhweqVHCde@ z{KM?Bo?uJ1OAg?nJwSX%4D`f;Tf*!tj>FbI1kUA!#9s(nLfME&WaF*l&+B)*(h_48(RDB5&t9~B-B=}6T zR-?bP1>D{8xE)wIGYb8s<)&wkzm(9pcH*Gab7Rq!Q}OgE;HAB;pV3(iTOs3gQyVwsWgOsOJvbh*1GoCTGUmb}9P_|^ z;W7U!b&-w@sHFHHLL+CxV8UGs=9uA;j0{Gmf z&Tym}h$rDItLWdTt71D-ZHtvhxjqT6;7o*DPF(Yz2$C6>fD}05=O#L3O29Vti+h@G zHVe{sH10;F2Y2GkY6{<3s_y#n0=R8@O_F@?R51 z;+>MFq}d+2HDNuWAJDyaw%vIpP}UZ77z4q#qknpQVuj&R3giK2*ljTKhhbu%FJ6xL7Gj22;oU3TK^uveAJ%@cEW zT;GG`%v>CS^@H^9p3^9QNBo6%4g2edaRS4K9dft3f1#5#^qX&KX{N3Bn=67gZs4?v zotwSQapNkCna!k_ouIG%-2-*Xd<0+G)ArXNKizLJT#y~V$08-D1f`9f!AV^=n9Xq+ z$g>@Hk6vjSgRfToqHZ+Ra5CDfWY+i`o-gQYzq^g%DS@M~>*K>; z%P_~M`o7(xfsk)Xt~ImuYYB0k_V*1Mz8Ts+LY=G|Zvpd=s@|<(<{)l$v?ZR@YxdWQ zGOGRDPU0=>ogLKMqc1m9V+YCn~lLigTj-J8|6;Zp-{z11P7?V#FIB%{F=2HGh6 zK?sWKov`G_5C1wTlOf}L^_uPJUZ#f%(NT%8hznhOW9{zU`AQnEMzntwM+g4K!n1i2 z=kh{x@j~)VTD%wMBD`S%cHbtrcpxnfBAV|) z)w2K(NFhC7+E|*ISUOpIAT_!^sR>Gyvt*_>zOBXOblv?YF;xi9)5IG|MZxc{Hb$y> zd?48lk8d8y7I8`cv_>ewFtbhavqZQH|t2I5%Rc$O`5mNt2lws49b`3D^wa?+G*gep&O*BH#HmH3B;4B0o~R^HF&v z5!gJw_rxBzN z06L%!Mt?6&_B>R(bA_Cj8v7$e=Qqx1{rZ^sG*tue3R{P(z=0*w|c3;&Q4qB@>H7rmM~!bJ`d$=E>Wv zhW8avAy?)!f;4!5BnXk__{ZZVk?Y^uJLYfY1pQV`dtf>Hks|i$+0y0u)tYf@&n@2I zqaJ*Bw|_P#C@VkL={x;!@l{ngK2~Ra8j(~c3x~vDPtLDPpbUcFKlh+;+X_0NYXY|b z{)7WFI8OQJ4`hIHaro-qc0>!2y+8jdJ9y-_1e$uvT8iHVy_`hCTXxQjwY}mIrE+bu z7o#W&b7@ShsL&REe!Wh3?G@|Z@15PU$1%RW)KEzX&M^frBKn_1!2gEwzf`4l-*V09 zDDOfkmRq7$Uzg5&Em9c9dux2SB5Un|6}}!fI4tJ@F4P2xeqG!0(%omiih_9!UDV`l zS)M8aYcUd>V!i6rGrv}}nu52d$SshXz}V{c>q z_)<9FD^VablDSIzN?m?F`>VraStv*IUsxd-J759)e}BIS|9}5_@N&c=G+3efHMJz? zBX2i+$&;LM@xef@q@~|0z3TQ2x9PKHSD?!L0C69&5KzbpvpqIDFlw_+mJ;dOxTc%v zSmDm&B^pfM6Kkr<`6|Zl=|(s4`AgpheNB(+Wlq_JmQ?C+8OpWcZ}h^>WS%kItRnZ) zUthn%iczNN@YVl@EzI5078DtR6c9VVz>(?4OoyM$b$(=Fn&wMh#qz$N%5JVC+UYXJ zz1JCrlr-eY{xX&hx1CtskG>F&)=4DDJIHbWTgqfWDY)xq$Le_m-)XIZG~W(c(ML9+ zveu3+c6LaHU(t^pH3HQ+Qktq4`eN`qcZ+)Xb_cyF=2lE}R7V)N0_2@w)}UUvm`7gt z#f`cXKK^!5V#nQe)h7qm#Ei&1*2@WaTr*vp^ zzjwU`(NqHhWk6GD7nfr%Z1PbVA&kXEoTQdy0FPkn5}h7@VMtDZ)cki{=CheB3^$+> zC00j!6HDOl*_$1qfmLF5!??24ReJ?3l8|>&b@unevmd8tzWZK(xQxj_KX53YG|eXO zgKDF{!zn|dvP zGATbNGU&X0t_bC-Pg3O@_*-|zh7s%WMU@us_{*Epk8LZei`YAFgas{e*Nm8ACT->Blh}gv z?_KZ}HSlu8@?DeTFu7UP$?mBhdD|msZtEHOX7NC5!XR&Z+R~lTXW)Dva-EDh4EQ{* z&LE0ugJ|ObHZyQw>x8yOXc_OjZ`=#mR~2;7=l97M`>vdQ@_n7~N6B^{{Vm%eYDOlcO4QLb_p>(IS1%aZu38jY07VJY)_I;5uO9pWuoutdtT9{;i-H z`MqdmGhVD&{9I4(=(cO0o=j%@8)KV8xR`eplv!@!=~GqLR0K8{4{6F`-`J|V9af+z zJYK#k3Zi4kaQz96n}YN9f5JJ4Lz^cqD|)_~e+KWilbWYqjlXb>K|HMVR0iRfa6g0k z-7WfeFAz4qf9dk?^{e4i5#&&G5M0d-Tbw za7`4lo-R;MhZ!2I7}I z=@GIpHVa1b$;+Op3D*OJ`MbzYb-u^Q^rgvMJB6lK`oo6|X0hngyyqJ6EXZ1=k?5=Y z8}q>Yl&>8^dT+aP7X&{`KuQY#KP(&p2K)`qe>(`gUek0*zIDp4n>4w6LVwm-RW|QB zNB{HGxxueXpHy!^>28EP74~;M#%q85n!lJSZpKEte(-JKi?|EA)J7M0F{p?jH3DmRMPcm7QX&M^r9Q5OJc4th2u%Jj@65TLX`9U8wQncHpDrs z1F%{<%B&28>HMResY!jq8A&6%XbemPQxq5x${Z+2_XBtih=bAJ%M)6PV6#;Fj6Vs}n9_Rba%NWf-1dBIF@va8(m=+fA#xsR-?UwtwY=NbiBP)FSp z(VjQ)AFa!?l8Xa7lyRz^s1tkw%Q`wt*TOHO5r1c^vb;hiq|YH6ly<8tCb4Zl zcM_9Gk+MPXS9QWL`Og$!wo(R&&x262yq&qD2h0vwl}EM*Iq!v{adc9Yn|Jwbs%VE( zoj?9op^KI3TMm|+UoB_4uj&l9g99VX$q{Y>w*me3kG`2HJ4b58VPM}AHq5!OoOs!4 z$i7;k=E~%mr7zxdsZ_+lawZN|0s`#3$7R%RFYDm8Msw5#Z+k|>aXaWxTb8wYB?h*A zWGx)i<9}{|(6$qt)x`nWen(`mR2E-W8cI+8PLqD}!S0!*u;Q<_zNVo@6zl?UtRw7yenE82l_Pysf$ zxc%6;mQV;I4SV_`{{olwc~WXCd~d0y#Ni+vX5PJ`)dH{jFX|R>572qm5pHAY>}&(j zfE_dGH>$Duy<+eFRC4_8m-)c}x;S(`>M!fy2s43M92b4dXk3$1pV(r$ z(oyg(YF&gfj^im=$+=bBvLTFjPrt70`%Af6BB6)7Rk}a3EVpLe)y$9ai-!f>M?H<7 z3`2h2~+57E#7A~bp$19w?xIERJ*KfXn%4V^T$b-SbJ_m6CMt`rD-phoX z;~2lQ`97OCv#^MD?PU%hHo0)O);FmRc|x0nBcM*%;#GYzTeWuX!<6n%a4%U|>GYyo zFctk~i_w)NwDAovffPCh;_T>P@@IpCtnd>yDDszR{4usdGC4S%ZOQh&sbPh*`qak0 z(zoXlnD2^T3Rba>v1&gF&zHD~a$(AiGdojz3X!KZbANss*-77=12@=52@dgO?*@sVOi21D7=&I}WnZ3s#v)eQp&6u@oeo`0-xpf^u`Ut(}ir)2TUhbleh}8m;3jcpVAV`DNQcbzwFQI9A&VHTzY}odR;eyBVTzfHT4;# zUHZO`wQ*&E*Lb+TuXSrC<^31tBoL{oRF~SL$v_tpa7k^5(De%PXdhkB%9%+B13FG zkcW?gTU-Bt=Dj4EcUDc(Zh_6UdwZdPfS}h6h4Rb;Prb6fYkfUx&%r#T1?UL74d8+* z$AO!0!lDnKT3x}bQ|HbQ_`z~}pVM;P$(GjG*v0y>yZHD{@z_Pvf)bU?fVU6Mq%>2^ zYAY2>$fZXbB&g)<%DTYHXqmpgf}-wdfH3#~+Ubxs82$YMoP`^Id*HF~#hWpHU$4yI zHcmq9Z!DRZ=d?Kb z=n|NRbdtsaW@hGLebCVuN-dpGLj~Mu9=k@Wqn|yW_%=TL!?ztS>zrsh5&KCM0y3|5 z!Sk-BMw-0?JtTJ6VUt-4!^GVBnyIEjme*uCubzE9wcdd^$a+Bh4@gkn=%2OJRhpia z4T5O;fh#w~$l$!EE9ASs(TqtA+IKx$y%3C_bO;iI@)(O$n$Bs{=M(u)1AoR?EndB@ zP_a0b_IY1)>V4`HFb`?B18MWypxOh5=!Xe@Oep4bLY#sP@kdbQ-Wco!eY;`#$W&$W zd~rbgoNpK#>vYO+E8dze@^D^8IxWC9_%XDwGEBa)dlh@$aof zUv5%%X=_%!HBAzYDVo@P8?zY4TdkA%{bzVI>`5JR%ic| z@EI+EYwP`YU-hLoy&dNWPuV^Q$kC;59E{WrQhM=h*VxV!9gErU_Q&*Oe4=;p+9Puj zc0PW=^3)%Rt{Ud8|6F_AQoD{GWh%Kq7SgS{TvmMNRW$kep>taDrBKWV4Uh*PV97x- zOo#(8`j1xn|FQAEXr-{l$B#dA36vS2?@&$i4fXn2>A41()$+k7{3W<}=92*l1W>(M z;Zf-O3tLLnZZY0GQ!j>BBkgAg>10D_&$gVW8j~IUQQ>>D*NrH z(+Y65UJ_c;#+{m06ekguES|rxvcYjfw6^0lKc(kH8z)iRoO6CaHVtb<29FivSN(p` zpcG9q&Qr7$`5>SJvVjQF-~oc^fI1lcy(FnmWXPnXNU&Kt+Q4&1%Pu;=Fz|S}1LB;v zN>?vV$wEoc-CYF6@v(@+AAIeJ>=v5IiTp+`+V5$W?ASK^H37Q>n zd!fVtBvF4_C7dwQ8{%KUx)u34Qk%InYuk7}AX>e;lx2PukUw!TrGDJ#Hl3P*+Eq=O#>BB<5b=qf)$uP#)smSW1h#TwPv$`GuU(4j}rZ>fXq$J9=-Le zYtdjH(jI38{`?Vo5NJA~SLZYKmEzWQ+xG^R<>m9S>ZqAcSO#qIEk;(tjpTMh0u?QV z42?R!xr+4ZPc8Vx_Ta1C`qsDPr*&TJ(QN?}@l{Z+3laU`-5dN)$cqj1MX@unw=}i*(;o#WPr&O*7uyyePw%XTS4UEd zPsPP_55qf{78h!;$Rp4sRpk)NAPll zJblC}9nD)B3P4{x`@>fr=Nv_At9W}*7xm80)g&wmf_X3x$!79!@-TPefzCtzspCCq zZCSu&du{Ke3%_H#RkLy|piJeMZgK5| z8(wMn!Wlf=V{mhG@Y@B*sKh)V4jvB{9XPWJg{S`>Bu5^-eKG+W+4GgkS^D)u_~rvq zfeRh6C0Sy2kx{+g6X4|tkm~D<68l-(q8U$FY)46B&7B_^c9o>Fz%_KWlni!U&I0q0 z79F_%!NuvY)jHVVP8vkvBBR6cxA9}|!uWF6{PMT4tNVt%SXT!HXx0QqjQVsp3e$@# zMJ4xKq*WpVYu+&<0Njiw+_WMt=_x20lTlm5SP{ z(<2kQk_OnFTd5IB!6YMRnTxC=uR5w6#lQPZ-}iiT-O%lRR^m2Cr;Xh##3xeyPUQXB zTR)|%bLIvB4@mhG=)zwoCrgmYceOwEflqo9L|AAk$(`n5<;O;tt`Eh!AMi(L4cnXR z^Lh!aK1`FSJWr)B64qUZHq42sW+#rpPZCjBqSqG~cVYG=eO^1I)-rT*4gWtZ8~}!J zg8%VGh-;P=7jhJas*;Azdi~JxrDiODz)ZrvCByt{B-~d$>jof&kg3x1N**G*0~S+k zk0gxnGI{3e=r+u`l4u0(dqgaMPy?58BSi~rptHb-|M|1}AeZih+g)fv9T$Oq^W866 z<>Zo_9!=#_+XO=!HN)PDJNJH9|K2a6PSdlo_1eZz7}w^SZl0YP`1Cp9dD>gni>(SM z%C{1smLZgq79QN{#^?hg=;vCb5Mu+CfKlaFOLorF6N*Ki-QZuVA)J(Hp6%8#6 zemP@Wh0OC1Q_n|yfcjb)8vR$)G>WzsQ$6evnaiI;%=q$?A1$V zPGvk#tu2Ei?o$`rKVp1hyNYh}yzCGv{P2-goD4$q-{*choIBM~_B$mb@bRThd6zT? z>TeNcgJ2$#<6;V`q7aR;A!q%>*LV8qHFV`XRV%#QrnH*&i1^|)RS$-ZpN`z}>X9GW zW7t(QFN$K0C7#Z#4R^Y?D^l}wvQ*9OYxxH82h?~Xp5z=}$e22fAbkMP0d+9?`(^h- zCB5fsU!J25`kT6QWTu}*b7)2Fn-dbR?Z+&Vrc8VUFGt9dU{;;H{uSx|2frpdsHHT; z!W%`l_&rNzSUGFB*slvf0biuE?ckIIxM1DpAVUr%5Kida1=-CtUaH z=BHbwOO3H19|31RupjWJVDP#NRJ~TKPPp80~S6s5G==4 zN1HZ$em44TN)}y1Ewub=%Sd^rlTdcX(6j7Wc}Dl&*Gh1ga%BdreGAC%eHjzF^3}{M{k<&t`KnvK4YSjA31&3!zR0NUTgPB>FD%?S^hLKxM5aDC zOY%pSioTL}b@gC>o3I@3YP~5?Hk8BraWuPw)yXO(!)8u26wE`iqS)QR2}p$N@<%6~ zFzVz_aF_h^D)B^h^8I^yTG$O|hC6L-`PHXC85gukO9=mDv7EBHzxB)5FXKLWXh_VY z5=p(-{<%_B>|Fz1pWb!@s3ryD4{h*tEJy#ALO3NN`7BA^QrYDZTR)wET{zKK_DA=P zVrI;1HbN%P#10)o8F8WEAvOKtT}&W~{&W$nXC`Cf}+drtI9uDFv}U?^eX-cyRS zj7gk$RZVp9biQnqgc6l2;Y+%d>3V}pBs|w$Aqxuoi2MOiAfN(q07ic=OQeZpKWUl> zW54aEhqkV~Wh2a*sDVdl>^+S4AH8ez<}-LXLN6s^Gx3bGYsylMrVmIiacVP8FpHHN zabqZbqv~%7o&gpMQu4qa6o7&@>rQr-AW`q|r@JE}pmEYt;RyX})1S6KN@{Sw!_}%V z;JA5_>6JOlw_Ss0{rGkG-rFXLNvbFsKbY0bw{6BA%wJBLer*1__pvV;t5k}0q%#_X z2jTx<;sCS*KK==`#=?P58EpZcrwn_3?6Zj-%Y5%OY2E>DMLfZ$;;$zib0x1)bbS@HqRA&K_&HZ|{|n~Y@5-%>vfbe` zuTFQ-Y+*dolj*9taSeC&iQh!P%RdqFBl-tW{YfVLo7eXuZKh>4OO$UUwUW&me|+E? zQ+Rv&M5|9qbNp;@(GQP9sAhehUoXZDBdU4nuZ!NOd-@h>wa3xFg9AA6m82v8+~lmTV06f=CDzvvA}!xlrh?sk8603 zmkm>k{BEUw54beb;t{T#nZC1gujs{ll~0f`p#j22;6OU+-$Hd#jxQPAihi&cM)eY3 z##VayCGr$`i*D$46Vnr(zM=8dLvU^iglSCa;lyatCgBlb;OX8xt6<9`TOEO4@T;j@ zM-fUXA{hnXA}!E!^7t~llbTKeuSaDih`WACrBQ^gJY1O%zq*T6H;|4>MRq@LYNTz} zH*(>E+aq5W@qnkOVRV{H=Qt+n%*KshZobzle|qO~J{0i9JRp7m64-FT-+xuo(dg)(B(ybK~SjXP_0uB#n4$IgX}9@ zqu8uT@&&vLUk9To^8Sjr^{q{g&IO)CL8x8WH_&Tp7Gf^cL3*R3H43*o12PM+4yhl& zbie=@{k={K^|8lp{a6uyl?$g77pO8T|=iIneRT;|$d^St(7m|!M$;Iu2u`Pjld;c5$X@Om^m_{1ebb!E1} z^9A4Zvl@z3^+bgY82H&>dyMzsMn^s5`>z2(L(D@{$%@P%J?HlCxA(@zNpi8eMqK7}ir8;Rjum8CO z|3<-XRg<4|#vXq2^; zHD_i98UrCk2u>(Z;TRWs&bD9%eXCU|T^rH&*V_DUi&T4U zy7mnk$>{ScUwT^59UkAOZ=Fea@I`I$b~qFvAowp22ax@73jR&1nhP|GHjRH<2ELar zBn>TE3Y2y5+eq%tZ)uy6$mgEi{8s}<+7si~ z6|aFx6=WUd1X|TQfs6j2#RpWx|DkDMQ{KRv#%QGU|GXwSxbS;?smaPkJ)YJgT;sV-)s{u ztZd5jEHpSqa*7*%>pNQW^n0@wqYtnZ3Y?8H2kw%+U=QTIh z5jh8I8}@$x1asw4Fb}DE-vZ`@*gO7Dp0}^5|L>t+AF|5s?MFq*Xw+lf%ApH;{&c^% z%=#hyPfnDFDdXIV8Qi$LxCE2ECf`1%Q8l|8Sx>=!Cm_SCBx7m%HZ?ZmQ0Ps%tXI$sQK6FAw3d@3l3-d5u3ksJs`l)mMw_ ztgV64O`e|bJRd7l^qp%5%tJEO{#2?W4#xjM=!EuufZ6aPK1cHbt4FdNn2eNa;kXe3 zbFf|5skIwCPrLbvW+>lsr1$@@~S2r@!24~YK< zq<=H^v3Ra4=dmYIFue|N3UK8RzKr@kRClk|l};yhj(PKi!y!o1mxy_lT|cnW4yKkHX7vV)Ozzc8+?Ji^$vC}>g87I>Pa z^WrVO+4vOX8sYzkg#*CARts?fMt`pmsY;f;I!xx(+_b`t&lXWw611}nwDq8rF^6`D zJ-KlDsD3D>_sqyB3FGmI4Z+5|EGuKO3$`ng{^aBUJ;w&b2S%2TbA&41R z8&07K5IQa7DKcwW`pamqHFdS15$yS(Nd1uf5m1nME$~l3CD*5Z&3%x+pym6^ER5Yg z)9D&xdh!=}WtLef70S#Pdj@Sk4dW`1{@CtvO=e?@C?fl! zqpK9f(`UHTPcpDcNh8lKi&-dJQ|NTCMfaF!#&(;Aixp9+6PC{-}yuqouyU{XNd83H0g67^HxN6#y+m6HhFrhI5U z^L+hbFnh3|div_swUvAtFb~PoHoJ{*kw8Qa)PA1u3KJhwYl@4vwxS2=z1(T^el)O= zmP>8t1wUywdYN3?!t6#&h04}m(_Q6#I(VLoAFjZ_^+7;d9IwNY${#_mhH@5KZKhHA`kJ}1;W3-DZJa8OHNN$AJ<^bG2o0CktJxky?#hja7xqgB7`xDA%(OFgE|V%d zfw~GvDPbK_2hYd5+`swSQc?O_&2dHswUynmNfi#RsRd}Yx>B?`JjQ|En^O$m4qsq(i6jL*L zoTV8xb>{=BL#V9wgFWf_xHHYSEsI5-+ODvf+W>Ie-GJ^<&BA@Xn5lC>$3>CJmhg*@Whuil=Uwl;rZ zB7J$*GIB~32S-&=_sE!(c&JdBqFq?dgIitUL0p>i$~_8uOVf8+Y*O9tZBQQtSn^0N zq@6wJlM3u%9BrN@ZKIf9Ocd*l=edOJ3T`cUu*i zbY)f-W!Wz`FlV&Y-3>0l7sY4I(v_}d$u48K!JKfd&eR|B&Ita?!vSOmY=F_15#L2o2Rb*@hE(w|j~XqXVOT!`)$d zclqapF~x#$tq?kn%_pb;^!RLujVa8<69#e>W}xN+vIYRBAm4n#qO{OxEKWNoN#2dt z%dMS+o7)3~3z9j*x%3?yY!^$azLq5`4s&3rDOQdo1`#veBu9~T^O>bfsH-QMCBYS?+c_y(;fP0FlRY{&RVJm38{+{Jg37sn&>x`XL!{8 zvw<-&o$4e{NH0@rd=PnLP}&ZV7ou}u$3~Rai@^qk_y`7zh2l%fe8e;RcB?=`) zHG-xFD&skwEaLUW0?p?~6Xrp+{YbSoj0uru7$!V-S$-fC@jnQNWeLQjlnxx+?f z$E?0S-XjK!&VBE@&$y21xfv+qW^Zolc-%7YEvhQ&nxEI~vAPrB6x@+r9u@Y{QcD*SR@p*r_X zp64xNj<8=0so8T_-Mb%b1k`q1+i5p@vGlV9(FS=qg;;|Mt!~CmR=RaZouS%-Oe1g& z`vGC)}NOYgn;O)5Kg%?+j8i}z! z5m|&4GIrLZOwXWt*Izi*Sm|k^;nU{sqo89F^94F$faG~QIoxq{gdbi$ck+~EKp|B` zh?*UJB^u~|dJBpB96bV_GsgS+1 z%e?l!TwB?DXM{o$lD$Gi*;|CngviL2S>%82weI)#|Gr*dzxusijnAp$^E~%`pL5Q0 zp80hyBC;6f*J5==Cqw$~HxZTXz_ah#CVWQjyP+1xTflL^J|un!(!qiIZ;G3G@$YHs zripvM2cM#juf^9nKl&J(ITQYD_WaH(qO{*X0V%}kk?=Dfy1jJPkW0u+6`zi!7-OOC zS$o&5>?=mlFU5Z8(EkJp1n9x^1Q4PGm^^*CU*Dz$*i&rhT7k^#)u@0?mic@XmvWVAz}(fW}7&eQ1^)4YVdLdNUPXQp3&5*JgYip zmIN9okpj^QfpXlQ!CIACaTAL53Sb`6ZQB-P#Df^Yqv9}#==9Uo4fAjFE;W<1co#_A zcl*QGx$8IP2T*Z8U(8op945{yIU~qg5_{S6i|A?0rb~FJhx!0>TqB6-RrX_vt($-Cb&t-6?h2A9tln#KZ zA1bX-y_<$^&Zowt!YpNL;4`71K4nJ}Ra@7|v?UEDBI_cy@Ty+6P*88x6mI{ACt!he z<5OM)f<~b!PaDX-XY<7Iyko`RsE*V3>u_sX8MkRtien(s*p|}{_iRal>Zb4tnJa=KV)H z0nFIa*b1~a;^cyBZU=k$DK)o1>~U2_K2a)qdSjB{uX4Ze8H)qtfBfDkSb_-@J|ctE zO>74nczp5R_?6rsQK&sN&tCBJfjnuv| zGrzAwIcvlQLQ#<7a?Ig1EsSA&e25s6W2lZ3=V%t1jIPR~CuvcI=0CrFujgH3kMN7Q zUg?3IoJH(H{Q%9-Uy^Ia^8!3yP2CAVSe^}TwK%V8%V(pnp-nF27dtEHH)|t3X$KQ3 z>DMK-Jwcoth1RY{^B|fmIw3Dc`G5=Dx`KPm#D9hB)Xe;s^tnuKyeV0``pl(9fBnj} z8#V45j3o0sW4zD(hC10R(%{h%2nn;ddLwi(c8~J$d77^AT&M+l&GPx-z{&Z?i-F>E z(`8S{PZAht)m(R;pxy9B*PCsm8ST=`vN@1<(^K?lrV2-@HYzGoeh2cy4V zgq8+zuba}oF?rs_(fz%~%sZDF%kUIGUCC*ZR8cfPCmp;T0Z|iH99tlLW+f4ZSF3f( zcY%w1C@*Jc$N5dh`yZNh(UO9BNd5~`h`~vVXwFkwcf6wRwwGt@u#HTYzcZsT5qFHT z_^Eth?(EGqowiI$%^c3mDmDqp+M!4+52ovGxMu5v#hxGb7jVjl z@E{Zf&VN%7%72bZml(-j{U!GxwZ7|n_WNmF!Np83T+(MfRL6>AXROv3}=7VqVl*S78X792h+n5Zd_Kq)3 zE18tjH!(}UB)dwRvcG#i-bAFO{09q%fPtM6Xi>cX1elZ^3-gnWwW~J@x_Ca;+)p8K zQemOtBh$%yLNGHOy1#t{Smf$-Zjx#G=h2lg&aN<7G(JQrZ=MzwA!}R}65DsDtPKS2|Q50ou^jUVH|E-yg-7 z6S6a2#wWMUvdQIq55+EtWhd!8f@y>C*DBeNIa%L@dS!?_O+!(!P|DtMdnJw7HdAR3K6(7i~yMfFg}znI1^y> z_q&$QULvu_#zM3RO`ZYimnF#uH7UW}iO*EJqowX>W<(roQLqI5c`cS=T7wh;>1O81 zOcmexYuaNs_=#_*sy;N?0PbG2+rA<>!u~$eaP0*VpmS`{;C0IRE<{k;(A**Z1xq7P z=gl`C%BlQrXK#soRl>O|3+lCxtW5Ixmmx>-1IY{kK2Xr}f=P z?zDCVNSlBPXU0x?_9vYkcv7a4zjlR0gpIrlupD>X?^T9g6{f{>Srq+Db5$GrnyiVr z3$PTMfad8Zrl4yzUtzmEGH7nE9VafPUOWH7=nE&~@WAb@oF$SwSjqcxa121Mye0U3 zVx)R6+#e*gIr2e0Y20pl^ZDb+-Fm9;XL*~Ycga6G;H>rqh#P}D%}MFu)Vb9&a#*h( zPi0IhnZ8yd?WlLlGA(@Fv=(-q+ct<2Q^E`il3Pwb%DzB74q4*`;y4`DI6{T$pl6J| zKc3<#<=k%xx=~u&Mi4L&e3@J5eFbw5cWXJZ&$=!0laZ>I6DOw4?&8(!7#|%2y3rhE zL17%wgv`nTbl^PxMu%)_Q5El_@H{b04(1HDQGz9^J}=D*tG0+(s#8$kwK-atJgc+h zqwh!S7M2~t+ls7XjHEj{LU$^J?O|9A;nJb-_(LS~iwkJgVakTaCm#&XmBqatHlD(~44Wg7W&~!io z?kN>?-v&aaZ$cOaSSIcoh9zO=iPbMerF;v|#{XVqp~ZLWR-rSeRuF&4^-E?C0vLWg z2)L3;#g*>6^6gv%KCXcg)jfpy<&gLxNQd;n={+}c}ASM`_6?LBud=Cqz;c;7*M=ao$UC0Fb&iU#yL2hv#V zm$K<&)u`9c$BUd(3o>!;T}*Rg6>MGbuSP_u9#KDr2_B`#^G`5wDx^mTuj=bTy`7wK zvckCuUG}9F71ldb>S$Lc(P>_Tmm?HXNRcA<6wu3xc2A$^FAc!Aeze zE5(nO3Vc;aeFp8VTuq=Z))1JbF?@%!IIt+3zGzCQ^uygT@<%;)Sf}4>4_%#n{M)`c z_#z**nwxX$^^H$xJH|NIGIJ8&M8D5S>8TunG=>K5i z5Hz5a!uj|o(DbiuL+E+R27gaCJ%r@jU&4vJDa=iswz@@2e^tXZ9&RH?fHocSak78Q z?QsH!gcdWCDtj=s=eGCDYQeL99T(Jmojn0)Naa&N3L+@>2-J8eqPi-gRPSlyjsjD3 z=B!%3w_Nwa4e|o6N9i7%@?+{Y`p^!DYN}i#YRS8M0yrdz17;>K< zOgs-yV(*V?`SuNNQNTxH;1H}s9{vd|{@-X1XRX%T$d+M|MT1gyhU)q9!8Xl&0dHOl zc=8Wx9D#LaxJONQc5K?YfXeVuX0O+?!MVqck#mmr+M%5rt@!XtB1m3-dtlmg0O9`! z0X5*x3e?k6nyUgfq%##CER?dHfxhgBnSA1>6%h9N%2$3j+8SF2>2_xi+m60qeG~v4%dp%pm_S{BK>Vz;f;+dR41Y1w= zC4YOk$rQV^YGh6K?kD+=hPxD5~iTF2y1fPyuT;C|T+3<3vI>CJFg^ zX4@ZPHjYPk;mJ%?7g;%&ha^+h#x@R67;sJg-#cmo($j+;3y5vX&R6@*x;yXPq9@pS zOPfjS>3T=qbzkgO2NlzsbsbC=c9F$Ml)la79%++`Gj^wrttyXhKb8%T5ica7AGh_iK z_NF~oP{Sm{KZ_47K6jZG^CN*9`rw7>4!^iV8ABLpIR7mG=YM^AKnk&M-Ktmn5+!77 zL#SI#%^zjun_JUSZZ|TXYK&EDrDY|%0Ob5600$KBgBU_Vx4eUG3m$p{;!2Ni{Sl?v zdOYZy<|J|h(|g`!gVPdukz>gy$#Pv@xog=E$TUNsxaSDmnJ=uN|M7kP=MF3-mv0Q? z>2vmC9|@eHtZcT>KME5Y5xYURO`+h&)%dsjmVP;|ZtpIw2spg1>L$DC% z;c?&jQ}gm)ED$2>AKvzUSpKP=gPX*tDH~s3e*HRj)DmC#lVo=V7aO=zhk#5oV)IY8 zB6+^w&|-_LMpcp9BNRj3u4l{)IfMLqPFe6%5m`Nj!3+KK9l7J-+TrP(gMMRA?fIW< z#{0JEtf<`+#v)y3*|dyYi?T{wlN@i?JwuP%t9>axD`;fM?mLu6F&q}v*G#z~zA%xLe;SPa8YRH|n&Q;n0}T`~_Tw0 zuXLN(QT=`|Qk3p$U7j%pNxx!4YDcH1F((razx15@|FLlh+=15hZ}GXDY1z6;Kce#Z zWF_>k*@=kMJ6zOr=zlbG_Kr^U1d}rG{v80vBG@HhFmWqoAv%7=>t#U4v%I>@)+aPK zx>kOM3%^|T0`rhm37{dmxVgD<99^YP@1*yqSXRzo9L<#f#hw;8p+j1PCH&yd^HPvGE}<3QWmL)G+&gcsl#?(qp;Bc^;}j6Y?PUC6`c=$kvD3R?MCSf6`D36EVFF&&@a;0?pR(<|0D8DXmYj&yd1F*B^25mVY1f~9fvP# zIxknKD9Cg(a1q;hQ?XU6P2Xo>0P~QXUG~PnqjF>f1ogg7nRn(xxYqk|S?d10LOSE+ zRArjLTpnKCUf(m{X?x{fP3I8CK|GpAaxt)tlp!l)tb3oz6*Z&IKN`4mlg!O#_#DbF z_!f_KKpQ+Ast|DUVD$G8)e)OAeQz1f-Ra?2A5&5i4X6Il`M$1?)X8M}fpF`ZDtI{p zBIhdpHo`vsdjgj@ggtZ>QFIe_y4Vx5KeF5tD%UHwf#(+>8D&6Jr431xrWiiKruoF;@`Q>dM@bUmtOJN z?{4hi2bI2?wQNj&AdW%oL*mCEA?W`TNCpJj7qd56ns%}y)Jooe85(50p+Fq1KZ;A2 zM_wJzA9Dm!SHXmzi_P2mu7PXBid>Xv1p9rlOQyf^^YBWEx}8XTm$tN9dxkvG2xT> z5SW&D@5^_uP)EC;KPa1Lh$}CO=9OQ?ffX%H>CH7(409^sB=Va8X1WI zUk;L*0J>&C%w6>yVCL4K-^O98C$Nj0wnj$C>tA0SGwGlGGRL8+4j4XTkV!rlMxx34 zT9NZ%*JZz8rBRd!c5luWP9K#EpXJar`d3uNnhPsbh5ZI&mdf9Z!_@@B|K#BiGO&%q z*#M)zUwR!}ucERjaI6UZsz+jXu&z(A*NMBggzX9`*mY%)6qZ4SXcc57PUz9uYm0_n2VCfnpk?gwD z=lxN8I6wClaqI&I%no@E3{~<(P63ukFJ4x$gu0Q{7-wDDCS!xe5a#%w=LLAbkYr+P z!riIZhs43-;r{f`x4{`_-e2r-^S7RSa;Oq|ezB3w;Ze^5;~kZ=3oa6`8_UIyK$?O@ zl3;x^C)L=Rx#lPvAfgt5qS>0#JzW<~&K&xH?lPE%q)R!OJ2=`qIUe?QgC`Z8GVLaYUPpj*?M+o@9Iih|H6M_LS`g>VQF_^^U!c$sv{p0?L%O%91pJ`QLj0TUG z+SbObep_=Dyd1HOV`ki~KEt0~1^M>iHCkjs*uIzZ2hVw;n=>Q_0(k8oK!qz^`a50V)>1sTV^pJyY* zqKQT-eq@%|@+YYndG<7Mlsf&Xl>P%=jsWQl&h?g$wE3{dxdzV{N34Ic1o*`fd+T{f zzFE7ZCl_-IBst*witGyq`N%?yAod^~#mLCm>DbSKNp*(x?p$kNrZvBG`qFZOaf>vBbuzY(i({eI!B0Jdl2H()uRiQ%ifH(z7|$HSt)&t3v)We4|f?ZTjT4ykv)s zI`L?NBn%)Xz+HPxf>inY56yt3irQh}vTO*6))MK8#m`~)S~F_=Z)Iv}M$)Dr5ZTZS z{Bisv*W&ng*12!#d%!AcAhUuB4_&n6JFZgL=3MAnzFZfp4}3#ln>FX>KJg9dhGp61 zUY?PuNzh_pczKm8^86hglL&%7h6je1I57;EWPyrp$!2N{uWNWrhf?C}%XQ|PR64q$ zKY4}azHFYqx2`y4?s@BJspP`Pmn;2q z7xo;Nm-XMobF-@{fTofVD>JCQ0~0ZeCM`;KHY(A!LnOdP+maYV>}lk*u;0Ol9mL)({}1AXMy_sfGR4 zUrCdCWsleIDebD+UdC%ciIon0Sd|roz4NJY`l+7;#1edn6EA1&MGq%og4LN9ueBrt zL`Z&U4D$}8-x!8yFvq$KE(%^&uy=GYHZuY#el}dZoG1N4->C9eeMfI<8JtfqB+bD! zFTb9{eiPpmt&39?j6`sNEodolc;-NsZP^6|l zr__DCuB^|5wbxKQ*2Qo7!)oc_4lZbvx^lGTqfLQ%0Y5sIytd9`L{kT8KHyO5j3DX! z{fV9`mVjhR;~pJtQ=a=2{3?q9rwsl+IiddTHkM#rB05o0r-~lxGdG&_XtZn0iw8}3 zlVdrsLch_m`1m#^sq=!&<}%@%=LMrXuj~V$83S^69z5(ddgXHK`3EUAFb~znSEL^} z(8b+c44n*)g(~Z!YX9`=n|9~V8gJ7370rc&4R;5f_t?^lcrR_|-Hi_rw}4tfOpoWs zl1R_2+)nN)$-8p*x6oFKUEaz&CN=b`eg}PKL%N|<2@5FPEqy#+f8Q(Pm3!1~JMK6G zyB(|V7nXn*mOFT{Y_w7oq>^@IYW6S}6H|ztjg5u1gB{cj8lph*)NO^Ry6rt z$xcWjG8)pu@B!{{U;;bIqH(ccHH&nwwsw)5>`%85q!KktVMLF@zKbUr?W9*306h|1 zA*%OGE88XwiSM5@St>qR#i6%^#i3k4m#V)i)2UZo49ufnd_{f>2l|P-8H0|($H*nM zt-Z+`r6Fu464*-Do#Bf7QZ(@#e{Q6Uu2Pt#c7#*H5OfB%K3VPT)pWb#nia*=}#LOUWC)F1&>0iWjx6#A7#|l-GzZZ&^J-r;Di6+R{ z!9ST)qtZ+zZUHg0Ji#Y7=oxp(6gw8uUl%#tMNU!UsPnbs!+=78(80C684jEh9Hgy zsLcsEDRoA5&7{J6#ReTdxak(nV-Z8Hxv#>@0>)pFO)Kuq$pI4xa1fe<;*LlW8lT%` zf@a53lzU6M7~ziwt$i-e>qRR{ISfo) z=g^#79tPBjB@>G2C;b%fCjBE0fk7QlN>4ZjyOjncqSpwwc80KBp^d<)34LX_#9@p6 zMI^C^$U#UI9+m+*)^Xk7u6Ql75SEbKy|%zSYy3R& zrSM#N_$C*{8$VeWHY<3iJR9WYGiT65=$`Q9LNJuBz>DaMQ0Co%%g@g`E$;0_?j?7| zODP$dTUmiTDtkkyg9H5e_`{jgp{?EJ{vgP+6%@J@`e8U>z`KL-J!Srn_6pmBtbVuu8^XEgHp)##_yZM01 z0pm}sR;6g&jtTb!`b|pPpQKpHfO$yQS8HQaQ1%Lb&j7j^xW1m!8tEfCMt1+<;JJ(M zgl=Tkz24NtcJpD@s3yT>4&12r6eX#gBl`Xc^8zmxwpDfW84LXTY4preW#H-1q8JsdwoGg6=TqG7h(xR z^3Rno%hLu8`bcU7&rv$z(@FR7uVZ!+;#nNXSqCJlBPicLr;9G z3+*@(El?hRdc^F98};G{EDxBRIqSrBmy8cVSU!W2k_Oq+a-DM=58i48;N5Ij#`y4?<5(>2F|U7v%lFXFk~%6 zi0O71q*%OOpx+IX9V1uf3XF}(4b;d8v0#7y5`N9cJthwZhf?*=u>?^>&$!>>BsDx7 zFP~g0sJ9>3%I8h}NKTd}9XtH$gT=%VP-`FaDVLPEjeNY=tEr#us$Dl;;A{_inC65Ze05Ylf%ubP~zMCES9&po_F%a zZiV}Q5t^uOu^lnXpliUGNXpHKt&+PJ-ERBIC=|~wa4&V{6U9L=&jIm6kPhjC(cdq< z?DWn%DLEcuS&5vkTKGOXVX{*E{5M@#COazSZ1VV-jzFq?&!B2;()UIBhr*1ZZuL~= z)PpU%{f)H(5>YQ^JGeg@?TW9+2XIQ?wS=AA#ypK?){GqN{Ir57aIC-qq^ zh*|;gz&i``lY`x7Y=Ui;7R`vH!DRV2cgZzEtVEh~XA5y~Ebtf~#s0#UHMBAYt(qNR z5YCg!f`$=O4@Vzb*ws60eT0jzbQiJnN^#ykB`#3s=Ncm!)08y@2Zjj&o)ty|&DoEyoqTkMqUK@S3^epzOtXkxgF#Wbt zW76O%N}4!SJ0B?!3(4jHg`S`lYgSh)ID1R5anI^fxq5DdA~8(Gr48n6Z~P;+$Cg#u zM;sK&GIBgVj_wYVh~IzGy?Xyu+_~vNPuLr-FQ3S{vCyXx{EzYDk8~re9Uu-dNQ5m?s2opgXJe=HE{%!wu`K0D& zThYlkf6wFWSHg($YV=`JUTg+?a;|%*tMabLc92M_gwYpIDNy-xRLHIE@oe=A&yVpw z8F*<(!M&ADAp>XP#HI%BjBs55#BqaP$c-bX%Oh(YN>a~=-Z!Z1Ahy?7xn8s;gnyP< zSFj<9p~w+U)9)$mOJ=+dZ1<~^!tDU!o+uQb*gC7Y^xV*P5z?o`^YgVQ;H+ikmSEYT^i*T_07*1{?S(vw;GXXSn)Ib3GOzXcA?7zb2Fo zg!|YQ69Q9I?(fzyVR*=xX;WRJ!`Nsy(;|)BmB#!rxa@7|N27M%|Ef-}jV+=l8{P?h zz!zMi4)*YWi`n_WDL#yDxD%GirBz_LaG5fHlDBTo59Xy*uX*_$FOTIBR0iLTs$BfJ z@x=D~m97T3L_e&QI@hyZ5{Y}Bx~36VqXx`7ktj1mLqnLo5#%sp4sOdnrH<)`i@Epn zW}fwsVO88*x6+!=k1s{@53DtPDDE}vC#2M@T5hOKLB%i#)QE0o6xX0#SSXKu#2u8o zKoqg1lQZ=M9)YzEWCfyGD{|Yx&;&wn|5pG*<79cMj(3m{$rE7{u;T19p6P= zEA9bO2#G>l&6)l@6M;vcB;6Q35sUk)tRS(^!TTD;%}g%Ri`%te9+D*mZchVC?*7Kwz%oF$==oF8A{JDwAz!?CHR>s>5|TDndYrhc7# z6{}0lKl{zSFPPql;r)^kTn)#7Q$B#_h&UMiy(WMr^ws+1kPnB%P$Lgo592(<-~*J< zoRtDQeaNJL1xpXS90AYDl6^Mg?q*utwj|bPhUhqhp}yvgY2N*of{{e8_w-sgCu89Y6&{DnE z=w=S!v!~EDFTKF&A|jtMrg77rNkMrnHD8jEnC{yRZ@6EC@IQGtfb77e{f~8@`vrMM z_Np4r8a-CfWJ)*4)c;W3S*W0$w<6^7fxd9u=m@f6c5MgFiOVi1oe!c*Fml3(lX*$P z?q#+maE?007j&Eh^N=)5BNzkj*n_%TQb z`e5|;a+O16?x9a`RZA24Ix1qw>OIWh(^a#Cn^j#hWA7q-HIFTHgUw58_1*(vIFg&i zuB?90idn9w?2}#2&wdfM;L@s;049(W0#M+^6j4wWT&aL-e?Oa$pefDWnu6F~0vsqr;q?>|iBQ!Axp=3O1;onM=U(7 z)yeZ38Kne0C5&QN;?0u`9p-qVg{=ly7j!f(;FbYtL&`9-vN8wpN+!l2ZSknY5~y>| zamvd|5KTyTc@^IMOO%thZhS1=NiZ3%6HRxI6a3UR-!FjcANAysCzW%5^tIJd8(iT8 z!f~f5to!7oV+Hc7f9{35CBv5;0Wt?*e0bWynE<1|m!~f6{8;msokPa)k=ayge6?~a z`{XGo#9jhQcQe?!r#~FQ6|m^h!)E_zdxE{}&F}r6e#7`LY_reDo5Wg$`Nm}_!xQ(A zPCI*78wZGsIU*4je5s&F-YG@O@3y4z%Yx3{af!>2V0o_lFII~?DA;s%16lT#wx8B^ zSoXI|;?B0wQHg7y1<-%~P@O8v)JVZ2udHR5T~>)<0d7v9M(-hY@O*gM!5ILfzlZ7J zQxurHH+o}CBO#xA)V$OStoQ+XQ+g$;@{uBI!n$1Wa)eZoiL;z%FRt3eXW~go-l4}G zFlS+je{n`zkTp_%G~SvT%tM07)ELUc1w)RTI4w+5-L4w5hBF!QYXN3!iG;~d@^2L6 zUUYQ3uZl z(gY9O_&1nf$`?6mPzhpf#IDQTVD--uHXLuGbpUf{6_ReKdIG+yt>0 znR`GUS`a~x2ScBQ%N*HGlS0F*0C=oD5=_=m7}U@ZDGT=| z3(>Tc)(*w|X63zUAVEDbZzMr4Xh}!dPC~n=&6u=@POb8MpE3i(Zz7`0Hm04}kcSW+ zri61u{SYRE(di$fB{ZSQf2wZae6#m_;$SCOrmcSGeka$J%bB|dps${?3|xOjNE4M; zsPc2Ui)!PA=Nhw@=$|bUghrJ`OtHK!jK9s2BvA__I@(v{7I3pj8X4PLIzUe@j`-QI z4Qdr?jE=efEMe(bfl%|yxz})9nt$DR;l3O24BJNzXb}*I1#tL6kcxF(+iuyANx{&1 z#)KaePuX&7v+B>ofk%;Y?qqFy*)F)5eqs=Nqyo{jx4w7aYTKG9|wsn1e)%Yxm1?Z3-(&-`u+6#bR&_E(|lFG|xQ zF9N@~|E?8S#tZR*c?va|oF#7t-yTvFF&8H=K7(%eK>Iz^rjRe5h4*T{wLjXh!@d0m ziaRoO`I2JDOEYoT$kq$~Q-r>9)0!cQ1^QK1B#Z(gVk?pB+-ldZXuhZX5H;gmX@oj% zgt1inIOQF#t+dCP7+)q6_1L0)MHA=xosoN1yb;$gnUA}}&lsE|9u7tQK)2)rBlyO7 zeEs}iy!@BzCzg%;FYm$Y&37)_$#HlR)%aza%Cytef2>Lk6kX0?(*&H$iEciaIVK3w=*a9rpOrwfEm>-Y;j|$1S9q z`t8gtchyOQVVF~&69ce>drSZfP8-^`tU9QqHM4LKIWJ^L zz0u}+F&Yuy!P$D>24iMH&SI>fR*dvg{kE;7vz1#Jg)sx%0`woC9-KP}4#0o?!}0t- zK0A0hLcU^mQE6mjTog@sNJP!&JM}{CMWE)Z*toOh^x=m38DB43dzP(oka0S0wC zabHdv@_XCBcH9`Y&$(_eWUvtRDDS?Cyq;()E6t)9$n{zBpyK>VQG3Zr;UwLd?3&L! zirPO(pYPD3#=VfnesN~7^^C^G?G-5>O$xXT6>7!iUh!vX&4C;ndaS$lZ zq`0=F0=na>h1ovSm-!eKyoKJ1ghL^@8WSK<=>$%>MOXI0FSA$ktNg`l*c+rfR0eRL|5MvuebJ=*M~?fMDP)} z2{k1#MDiCExr6hL2fLr7i9IlSBAJN0R`siY=M|}5KKr|N9adwsxvG1-CLdj9hTNhON#}jD+AaDACIugp^OjcVYMF%gyX(CMCqU zruc0^XCjIB^#oC7Nw9mx%bk3eZ++jXEiJ%2Bo_^6Rc~Yr1Fl8*DgcK%u(h4iBID1g zi6)^P#?*Y~A)Ry1?3uTdb~j2`f9#30Cn*m#cX<_N^krSyGmfnzRo5EMoZK5`&0^6* z56^#Se<#eF-~$BVqB$lHxPq^OLk_^`?^gkLmIQ8-3EB5OHgor?Ws2lNf1JU~6gT@= z+oPM3Qbdk{5`5TBv`@>~ALa5!uJ#57v8um+)1Mld9aoxxS6v`s@oPziRbde((cg~$7qd=FNDR}V3%U44vi5`FO^n084rJ+aP3|JAq9XX zBuRpoI|WVz=+SKl*!E5t0_2}3OF~sPT7fp-uE~p;&p7J;3FYad1thO{`!z+S-Z^h@`_&4Bgtcqj%kYI;i*b+19|HxqF8*5_(1plEW+JY3 z1yxBD%smnvAHFR|*7;JEYuphX1FrstL4XuuKMlU`*~n^4%l(Z8$AY7}4tvmdnfO*X z9b5k=vdriK%4i_XCuLf!fV0oi1RitXXpitdAbjnoJ$r&myU`x*ZfpFwshd9H5yPWd z#T{2AP6PQE>o)sJ^{w)Fz|%bI_tgoyFNZW2viNElg*C~YyRtTJk}}VJSKI6eE^+&HrVnwC zqI?K1cr!8RFqW`5{nlz4)TFfwEVF{#26{howIp~4Z3Flpme2zOJL2kb+6q9yP=!$5 zES@W-Vnr9!b{j8G*s1c1VqvlTd1;*MDko?+{|m20>sK{FXTWr@o~$`0zn{Htl?Z2d z-6IMOGo5yMI2wp1-p4E)0(Qv7KLM7|4b8P6HYOBLkNIdtIPa=4-!=8YYq|5?tBRI+ zclZW$AYV;RT7jCGB7&21Gq_)y7cXjNq3vFyQ5nf7XlyR85ex)?Atk0;ncLeQo;>hP z9pMZ(ZMH_>ru#-xhfx31Ec}`9ff(Xe{^((W$yfE6R&-(Z#LZG})SBq+ABuwsAo6UC z+{r>;WWHOFw7-3}!`+QnJ7N<9-xKhW7yv`~u64)*82!C;g^kB!xYSP+P1t&h7v4-X zD|9F0L(}?jp3~)Su~LGA$PrjKYl58vq{1z1VxM!uT&&xFe)Ar}zZ^>7`fo{B_JjL9NWq>jl0t`32Ma{xwv50o@| z#%)m^mHUNqt{*>`P1|mflU|dgBpZpjGp&=wx?clcj@Y%77KClEQ@Nk#hl2AfP>)J0MgjaM!#CUJ$Xa1+I%D83(^Y-pa?y$B*yNzg zaf_qPRyTN`e77Z`$mx>;`GlY1p=hW>u`WHtER8~mBtJoVh$@AK z|4;kj7nMp29E#^T=uHsyy3ql7z|WyvfsOG<r>wx@`$_r^eHvxlb=jrS$wW2OGqX9jEy=%crs~Hx zuJr5-Thc6)%)exxD$59Y?eT1Q%Wlb4~ZW_bx0qK{(cGG zom591c6MrzEiC!qE)s|;p_uWR{7!!W$34|cXU>GhfR`gc$|E5d(wgAJj=K=vHbfsn zEWd&s&ryX>z>~U^QR(`J1n_`=oNR9EU}_I?6wQGKWeeqXv9LW9t<$>$!#)Lw71kzr zp1&SYXsh1CV;JOIi1JB47%kt?Vc|#mFmHDSdikXbTHK2sR<(!wM8Fzi;H>11WE+g=x zgl6Liwo}AhA<5QZ#MU8Nyr-_C=)3u6^5x(&r>AQ*=NS2Es%(1;^R5ng2KBHL?kI1d z;r1{(zp=O?X<;%x|IS%S=Mv7ww$t7&p&%qhq-?2dduDWb@;@YV`8Z=7ZT4VA6)idE%f_8* z2v#CjyrPRb(4ud#RjG8S?s2MLn_;lf8oAU!Q+c;_1-Iqb2EJ8q(Pg%uh2DjjXK(?19H{P~oH@}P0b0y#Xg zB&m_yx`TQW`M z99!q@KaOI^|8{xUr!l%>I*SKb#z$h{5UhhOo$H^#l6hGZ!_>ok8GZW~4>g({O3|Mp z7ST$^$Pjk2){v0UOGjYwNv$t5Sjo*2)PCyurq2~DVLv;fMLqZG;#uLbBQ_jj<}c%2o@``Esc$1P;Eb31PlDxw`5D zYv-M}n*-ixWa|{X{udNx4?;YSZNea6=#*#$Da(+AH=tVj-~Y{uZfJc;$>-MDC<@`v zFKRINu=o^{AB(4F%B8}QT`q`k@&6sL57cR@V7m!1+R9}#P&nn4NIui2Fk zC#(XKopnr6GRtw3#JI1zMT^k>N56=Sx}R(WjtR2$xZD45kcaw;(YAV-E7Q6 z%N7-AYPy-ceD^(R%i7DVTDf)y3uC3;zC|gkkAMjz%ek%bVV_8_m4meWQ!1U_0U0JN zR+PBdvQ#hDmpw$ypUqh+atavnSY)`}e46!hgd*Ph`xo~r99>kqR1`izNc?xvWo*~f zzT}!Q)c)@#@P&toAbkK3PJ59Wo6FPX(YMF-L zL0X-_%MnX%{U((zgU0$Up#+XGl?fJB0gb^#vLiv0Q|g0@O!}%bVBRlZkJsDXNtHr!Tw!BJ2tpMvwdz@%i4 zX}Y&)EoPp3(Rmp(vOD`Pihub?AspbK=fkn@3Co&6KR~LP`NjwTdiSj8vez}VmMn98I6S7SuEz0<_HhIo(VSbRaI ztwGN!EK69Fw}QTY8y+ZxkHi2N9&bw={9yF=+mgr6YBNl^-isE?Pd@0ZTf~HfSK~$E zy~y3W6O2C|dOZoe93fH+*VQ!(-_O@=G4z_doG)jKHK!~yG4YqPri?_9;;GRA^N>tY z_Et8B<&5C&qEl*_Uaf7Yv?#PGgCE~1`ZI-O^e=1+EmuZDan`cZn7*x^!OP*FMVCHT zX*5t8CE3-!*mP^m9v^KsAO|mRvwu_I4I&jAPWc!f1o3}@=Y>xO5$0MzdF?1w^0Q1> z7G?V{nSBA>S4GWQA-OYwvPbZ=i9_gG$}VITet5E8uJ-%cRhf$M3nNLJ;b}HGOgeJf z;430IzT|;KStk(o1Dd?UK*GP7CCuLDAS4|eGQi|=O1ITx_q2;Hwo;@^1e{tNTPc*N z1?^Y)9wieCPw|D~4&P0+aC9Ru)p7q=7SEzCS=qSDtGhy9z2{abEt;-FgCQc?8auh0;cc zt)=HXchdaDG8jTfK<3~OuKySMC-=wv*mqdsV|uR)w(r;6Ch}(IWIQyJ`jqyM_Se;%?CYNRF{@jtukz@He`EZrWY2fkbidw%mPb?c z%Cvp|`-0owGc#0`6>0_Hh8LV8>i+@Lzo}Bvgz@}msk!bwj?YYXph*zX{0*Ueu)p+j zU1@ zMUSx?rNhpVTHOt50;CYj?hAweI;}PA#>E=ZY<&NY-@)$6<5YEBU)$o9tiwOEPJwwy za%2s0aW(_W0({AVT^;;7rMBt!=Yc1eEEAsc8T*u0YHk@c+~0--r2CL7Q(G5EdPR81 z%x>;|*-*4D8yPv(;x3LBb*7Mjg)3uygOWezH6PFGs-SsQhyPyEu{lMw#H%o`6fqjJywu zxQtet*fX=4HJcv_10FDsH+O3o#KOiH6m_t)H*@~KPc#aaoVO#rKghB!LV7*Ha+6H) zk7&LZjT3`~e7b_HNfGXHtococUI9(2yKx1BI=Hz)x!S#i_bihIi_@hrm|K$Jx8pPa z!2L_pfKGbkF`~oTkN(Hg|c=V?gK&q7L?p< z)2BzcI|6jl^ox)xV(jZChVu(DOcE*5id*7cU>?#P#?}FJ`a&2wfsPDLom0m1`^4z* zC~R;x2<+OeqMob8x8q~yxMBF~>pWjsOhy^yPfw@k@$$?s*0Wj|cnR^J2!&3}x|v-J z`JER}^p*sLO%oorfptLp7$O9DF#3C$^11lkw2p$lxb%V+t{}T>Zo-{JN<%^6hhKTN z^qPs*~Z~O9WTsoMCwDiDvW8}(p zkQ@SEcc(VC_@OpwlrdyR2Qv@*c|H&yl_W@yO`~bV!pl&Wf6e+SLrqL8oxSn%m;33w zT@6`(io<#=_kWzbw0I@rcX8A8!S{%zcSQXVrb7n)38p_^N@;(*5k<90!B@RRFk(xk z(4ywjHzkNxY<*6s!z}R#Cb2?ylD2laP~6+L(Hs)7yZH&XOvY|?boH5YipgAy6Zub= zKpYv!Ixzl=p7+Wa<><;Uq*_=_LRfw>h?W&D6u`!#>J72+W{WzRnPp6T72>0%RSi31 ze`b?Cx#y>GjYMl$mqN>P@|KXuS0T7A1$L+d>frfg!Tp={PA@0gAAh;c?%89BG4q(_ zYhUT>AActCDq6o0dqWnV5q{7kq-p2bVoRrXQWOcJbu!DxcuW=Fa|M&6E~M4czk<>% zDSm@_CkdlQmX5HaG$SBM$R0igeXVfXECG8Lwrmok^dPMz0nUZkrsjN^)SIf)i(wv` z0S%L?3AB4PIs(qFs;KthB3%|)=!0DfW1ZM$^;7jhSv9&`Txh-TZcv2b?w81G)gFqf+B)+cXyY9fP@1K z-QC@t2944J(j^j-N(z!y{^@ZE5Bv+ z0}E!Ndx0YbZZoSlwm(w%R#i;fgTS423vadoY_?7(q|Fu}KmUUt&_rkGWdz z9#Fn*S5@JQBA#b4^y+b9VQ%nH+-|UpGZeH#KCQ7Q>Z8c|Phdf3>xII*#_A&hzE;@l3|+Wlp_JnNXU=R&l>{*n?GS)X+S z$IQXkt#&s)P}bGX+6=;TTK&%zv1cB1EwfmLb5oL?tW)Be6Kk1d~}OIkNt7c-0|8I&SM!AKvX!{S%XOnC#b#H zRJBcKE0}Pu#UC>!*!`v#<9q%Z^_Kl*>&KUPL$)p|IKifZd%(PGC%=;+0a4`R2@j{M zytur2;g_NfiqkJXDM!l7d5(`{%7(X=1!f3YLr-xoe{wU&mO{9h#VwHiK}~?nI(pFaU5t&o z{YOuub{TzJ70f}R94tT8`822arMd?$opueCLYSnvM{<+I7i)TC^aFz*pgF{4GPy3rH zqYIlOU-YhaX6`i2=$f>4Sm*VqFzlvrM^(4}V)U#Lr%@v%4GJc#dL zGN|>KYERR@Gi@$wM&G0=Bu&2-xx2dI!n%Ds7Umg6gEIg=oCwyFD%UeJ@;}Io`b=V0 z3f+cX=F0n>qQtS0mtA$c$R=1d#$zzu>gyl0AA|LB=1_@1?aK?f=E@&Em#(gw%LpI5 zHGF~l8;yf3_%KoifQ4Y58rzuIIGH&?OraKVF$+{0kbri^cA(*V%gXeEC7ahb4vKlM zQ@aXcM+J=H^(YR&TJ$mOJi{kZNZ3v6BkrP^sRFm}i@I$Q& z_9ntb_}KkK`h0VlZG+ieUtebC+v@ga~zvIGcW(c3G+9m<^H_MUaL-Hsc~2R9+qOg?Q?^U>m6XEaYoC7GSOqm zCRgHLRQqF?gu2jS)dkW7g}>|F&k|9eG5m^0`Qt8MIoO9_syey=YY2uH+*IW_W8f{I z#EjlN)QnRmcBB*K_eB+9FL8WSh?KmKoIDxzoz~0Y#gkgfuKBdZT(NGY7rCmml{E)M zMZ@`%-$v=#MM$8-UD|NcM=%{x2dlq7Q#H(6A}r|SANpR+;Kf~6RcQU;%}8cI?IknQ zaA5EfW%d}Lp(M0>Tpp9Ag&&&_sM7HWiy=-K2X~vcQH%u$rmV(+*@Gajtek)?Z*6Y} zYgTo1tUyuaS)H|yhxRDHdMxQ(|0yRGtf`o~BA)j%ex9@plia@5;Ih(&_nqlV)5y4`bHoYPb>His^?6b3@`FFmMDGI8!hl{t2vzic7jZQ7RKg*@8T7 ziMjokhF&yX85Up_l@2meX?r1f3>JUvr-?|?OAokYp6M;(6MC}R|8mV)p%W_Y<-2;3 zd|?Lc+c+^;jUCO7J=%c5GiIOq?RI<~hzJk(I9b}-dCh|bq=8Zj$4A&{uF?H%V zop)0QZ_VB__OZx~`if_KPFt&5n$KL&Z53N{{AHG zT**^im@u>9U)Ur?krF|9lIMpScf*(5PTO5WIz|s>QNa~JdVQt`Ma*_L>N7@M<*Neh z2?^0_%Tknm0`}q=?Q4`_PAi1O5?gyab_gHWpUVakPtWSi4tmhs^z;2opK@m!kyqq& zx2S>|9%?y|qLmLMd`WF`OVFmDW}g3E@+d7=J&HLqpv&IzHfOvj-8IK0*)7zhZ)`B@ z6yu0E`1l7TIDN4CdyvvYW27&L&YQk>LkTrAA-9dXmAML#k0Eme;awfqOQD> zn~me$YU9SjBhZGThcU3@ir#b0^V|3<4FX>S(h{H2?n~ks4=XP*3Lw98#t)RxxJb3o z@@_9-7}mGo9E>vfd31(=gX36e|J}f4t@gum-2FPT9pgz9LCagWA_&5sY?ZtYoD@nB zt|ut!Jcb8#lG6@|K+ zw51*sahFB9gQp2;+Lh8@RoGt(+b;(N4|v zwM~S(Wm8*3QAk9*qTh4+FscolpJgY zJTRTeA$MvkQ(IFrOB*LE3u6urPa8L=yN#u-DUYXBgVk$?{O_tGGq1s%v;KJo{s(=Emr~Bmbq(K#z<$g zm+R}d6`14T;c#5Pcs(zaUBBh|Y&GNmQMnjkG?A}OBfb@HCMTnL^>}O~xXHZ(1^$M@37rdJ}{UeUAb7uB( zla@!YgEnb@-?B)tSJ5IQ88?7@JM|TJN(xMvVxQ87t>Drg&H`BdJyaQQv0wCt3%j9; zYp-K!83Zh<1U4SJi%L^R$Q%25hhgMcni!laZ z^1*T|F8&XU9D#Sl%RhmK+{@FVv~*_(1G)EgoZ(m+wznfwd)5IJzlBfe6BGT5$KVOf zy)=E5eR=;{y|WmV(EQEBtpbjhC>30j0--bQP(2XzMRa`ILqSHE9cW5x3p&xm?&|+Z zJB{#9pHTbqJCe0No|!9?{3TV|otF%e231c#WeItxd>!D{&$3jYBd5JON9H1yRN+M| zFdnO!(=1vn7_#F4^%8)=cK$zDI0Eedg_dfWvHdiRSS=~}Gxy~fDXt8OF?a8{%IGOh z^a;6;_MWU`z`og_&6RW>qFFzE%*u;@C%XvAZ0@bi-fH%;kfE=@g(qO&Y38_v8^jt! zFgdu61shBYI$P8!gj0EBw8NnMl7HhAj<4^Xnd>5R0S;Z~EUq?Uje(nGHsL&WBmUa68C^|XR+wPJIw5}q6ntn4+rLGg@b`K5sYgm`_S`-h zJ#kdI_rsxTo0zVCp(Fw~_2F$3aNYh~OOk4y>K8x6NMi-6=Uy)I3{3Bn?42V?cwKY_Dt+k;= z@q$msKu_Ma+;6P6x#`7bw;i=>$E}y@dEc{D3u#5AqqIJ~xhkDf%M)Cm!zFF=EVg82 zx|Z{isK`EC;Gfbzf$HBgF>*4AZv}K+r(#UF@G?$7Z3|6?i_Yp?At}#-omvX@-PL2L zQVc_|t={3QamuXU$EeBQsZ}Aq@eS{tC=SO1+51XLNnjs>Y6dkmwQw>$5m#Jrcla4} zN9|yTl}!n@7nX_%oJH(d0#?X!q9uLUJ(oj*Na93z!@dttdboD_MZdly!FT0WYDGi) zThgC=7MveD+w5>v!aYr3#x&+J^&^;I*2Sqa{0vFBcWM&ntC@9d>;m65Y1#9dRta3f?BW#fk+VOk+!hiSLdn+%&MDlTM`j9^b94ch%n5p$ z$7SUp$kw3bP+?3FO2-xI^8I@NA4)$<_x}5b55#VbEa%IcLf~Uhpr`)nz&?2s!YNXM z4nL%dWWCGD2Yfev*L^n9Z|IBGR=R5087S%IaoRTaRz75cNBM*IO{aqQHhn)u8aB!^ zyd~82$<^LRUE?HSIL1o)!p)IiByVR%`fvH}js`LI#1Tn(6rO*M+h!=4TSrHu9P{Jp;oKh9Na;8K9(CxSV#KV2gg; zr@b`da@|CcUtoyTJLEz2!hJJBhXamj3uq4v&@(||$zmR9Oc@%G`xigP;4=@&BMS9ocQZ}xm$im%AZ8>hoowgICl>}(*O zo`AE{zWy)fIpjXp$Kke|e^RM4SJ=Yv9_f)T_4Xs4FFuqRn<<8&QjSpQX=kQf^>l&J z!a<`uF)yzag$WUfpp7g(+B(2J$+l0Q{EMH3jnRj7 zdGDq0&DPI(vy&>kjdV_nsWlo6ilCE)lbtE}7jpW?Cn5=N32-KHHfU9ctjyz%ta8uB zTk@}H+7uP^i-EMR=HSf^mXQqMqa(3oDsws*8Jl%Y!Mu^60i3&T^DV6 zTO}>KV^M^@S7707pzY!Hw?BKPD&~$a$?YV@eR<0f?r0Te^+Ul->Xvf%?ZVZ*{Cz%x zcXd9cF5r@E!kcLa%GpC)tl8PQPV+mR>CuASP`x(LK0E<66ZCx{Az-LAcYN<6!^ow& zH-^)Jl12#qqFm1#+P^ouJLXD9bnU$b&hnl1DE~+rrI2{?sU6Scr(vOTCRUCXP9Uq` z1gE<#0_7;1-zAg32>#$L(8gdX5*;(?Rew;QHn_U8L{umbW_yC}f=&ocqH&wvZg>@j zekFE%vxKkEg3XzZVmhLr*hABm#e8T}zN`cR+AcrDk_-HE+My;*XcJBQ8L1-)NxI)g zf%AI@TH%6z|1dv&m#@DSTNGnLrA!obZ}`_nrt`}SX~u*!+mD`U#q-Kzp+9KmQOgcwp& zd&@^P@72AH?3MGF)K8hPi+fAvyr#cah<_&#TCOn;mIfR_PVbT?vy}1rgv)^ZHCL&p zgZ-1F%pNHIcdHjKqeh&IuM3Npb+(2$I~kw;4QM=;cYQzja2sD6YaB(w7!yjjm~C~# zWJ%gg6^6)vpADA;!+cNpHzHSBS02L0jk3OG2!>Eg6YnpBXn_{}Db-t_NDjut&qJuin5ttY1G}?-lM<~z=J8ih z!Bc6(R{*cu({P&=5YZrnY7TJ)dM{jq`#)rSgw;aTL?5fF+DMEAwvaiDP|^BTJ?r35 z5gMis5PKHyKh0|QR-fkiOaw2|XQ4pax(THx1D%^Dn3DQw8UeUZGl3@^6#5_9rx3x&gVo;~1dr$#_g>uigmHLeu2hx{4hysErF&Z@^*PF=^PBIW`Z2;UA6Ch&FNQO{*~?b8v#~HS2eW{Hd5ZA5 zsY4Ynf2Y6^tWJi&uCjWz1ep7B$b-nNyFsCPgBs(pfk$rD+2Rx-X6x*Vvc6 zz|b@&{ZU_0fgc(yZweE07QjDoLP#;`J^G=$XLCm;gvK$stAt@#G#A6}You)i!7Pio zi@KscNa=BL24mb1ax*?v1V}L(QeueCic7VMr&b_oYE%$7ORC~*u>|P} z2n7<()B7`Is)JA53+)AwMEM>>l>TnLw%#7_P$zEF^?Zn@vN4#*W^G{&o1_Fi)dlW( zCA{)R6Me_`I)Yk?p-}ZcgSglOJBg))#m86VXYk$y$^hiRboP_KruH=n>4)jPPOiN& zA)Eb>dSY`!x)UxS13tQ#G!_EyBLl{SAFnB^85F^qYjaqmMl zpJRp9<$*$P1M|mR`xgxcv$rn%+{<2C|F&6vxm4C$?#hzRyksKmf?*z02cKb5=!gNZ z`ui(}w#><-S3otqYr&#>E4=Y8=JlNrMz^k~9BU85pI6+A1P_N_G4}k3@>d^G_}FjK z?P4(CUY7Q1)qcx@BPXU~jcmM)76JAlgk;UZ+{WW3W}y7{jDDN|X*3PJM*NEz&%Wb_ zCk+h*-rgQ5h)$b1& z!2?X7!AXM;Fo6DFIJ>e5t|}r)x^!13-40OhO?AFg3)5m+(l;d-Zhd9s-?yioiY>)vzKB;ClF-}S~DyZ4GCC2WPGeZ6ft z4dbK(4qt!ew1atXz-(d|?Nf+A5&OT@XQ$n)C0eQvN(%RWAu6DY9xiO09XF8aCmY~T zH%`{__ynT=ZFJ>-zV;D@U&3zNpYQUF)gWPxya|~57R0y87~kno-+~Zbuy~lTsQK<% z1af8Kp3Nh2?XvsI!Z%|1DW0f4tS`t6!M9~gN`}0him*gM-!v_NMbmH)7&rwBoMwKo z`uht<5WQQZRYEU$KWUs+m1E}lH~!&kwH-;F^20SRd)jF7z{BAeE^gl;ak=D`o9cN1 z4hjZ~Xp9e@<~uHWw&@#r*xJSOu+(LQ^JEXyBWrkm4J^6l%;R0Du3UH#Y>4#F&GKb6rag*$w@V=Iek*td9qdOh)01< z08@|9|Ih}Xj${&yJXrlbMA{hM$@0r>?Ry1QoV)d z-9s^~ZAZu!;f})R{R8lDI8Y>%FI#)$wPO5vu*Bl>FHZa+V|_=%M65wX zyWiXI_iqPTiPDxNq{B^?-%h+!&#~^wEmb;qjlyb)Fph|Wk0%10{ogVnoWAu$dn$)D zuYBklpVtj~>P&&ZZm#J@`sQx_l8*=_%moSuNp_r`dI@PdtwKf46uWevm~6eW65R&X zSfyf>UD`ol4SZ1uPWV3)C+zJ!o&L<_`ID@F#-Lbu@rt&w&Wj~m*QvZn3@XjaP+^=; z2lt<;kzD%5=9VBtmuTBf!sGoH6Q`iT_yDWFzldGIBREKA-P?m~l2YxH=lY2wSHkU@45^Fe=nMUyI--G};!s<}jix#&{9cndWt;`lSILDZ^XhmmcWR-RUe;!!v z=t`ufvf6Ufa^j;N(5CQYDdSvHx*l4jnQ`zTA665|xzsxxW0I z6l9eGS2svmo4#&=eF!ECm=f!02xe)Y;B?loYHxXdGM?Jk0rx5G zzZxd=GdpX!TAOrxcjgeH0CjNM40|D zs_KP5LrXJt{?Tt~_l3)YsBi3^#!|j^YtJ%x5Z`x0V(QW{Owpt}QWr?lZ3Lpb#-371@!DKN4@>!%BP$8Io61YdD>H*z7>` zy;>`QwDE>+rDwwAwlKzW_VBmr`%E{`tg&@_RqOA(-ov}K|EAJ6;~`6Ieg5n#7)EGt z(kB4Hse{$uU$}MY@br&XNew>vUq)D=E_nHVNQHHl_P^OF%o`H3r$xZS;pa$#?t;})HPIk#xr3&Ah_Y|9U)Gj4T-b4BfN17&@+K>IHP+h z>>jGfs|DW4W9+US{RcdKS;3dLy9n<-y%lFrujP-7JAhRFX3W0U z%ZGB_PW6pxesb;BGuYk7L0|wZVbbY{2NMt?@5#2G5x2dqKw`W_2^jJ22 z#6msWie!ctsWqcQ{qFq}T4qD1q}yKYUAh1&v-k#GyOW`g+!0)xlzR_V@Q=S*_7#Sc;53k}ar>?Cg0jn5HHh zGUsIQ-i#jaHvLNXc#q+7^ObyQ%wD>-a3>dRDCOhZd5N#RJdt1;C+0ErBbff2seiNa zith)uqpDFes%Ix8nUg)9P(k*^kH5b6ax+5sV`?)GU#(@&g7S!1E!uS}H;D_EZgICvRqEKYoUb3!UhH*$$Trrq`1^I0R3Gf>p}|QX z0dzzitp5HS?XaCUbK|NJqF~S7T0|CBCy(i#@WUc?-RLsd`5++-0S||tqwowh_qi&2 z7K^C8az4tob$Y_ks~S#)l??Zlc_@iOZ-adZh6@-14x7t;^rGWuK5L)lAhzLp)`{Vz zM+(o%y}w+(+EO<0%4p&V$KxoC0Z*NW*9VQ_-R`h!*>w=@-1r>A(DCf!Lk@%aM%J4s zLOC{8LnbgQ7~_aI_z1gf03;ZFu=;zDzK24XN8hrq*RNYNHCbC|4nB;i$9dDL>sdoh zw$rBv(>35AY1MKYlD4BCeCE-ANJgPEq(Na6`C zz56+vrnnEwNJDU{ICeJcKLXy6i5ZP{#wcWpZF@{87r>i3F;8KytzydE@5!lV-VAKKv4(P;v8X8-0? z85<1wzMuVZYU}zAq)uZENB4dyWShOmIPX32HQe)*_W&t4M6Xv&V}*b0zvJMwETYEs zZgOBDn%|Wc?#5p4YCuU9fW2K1a^1l|lH=l8kgRh?ZNT5CLUIo;DQCmnOD5%<>lK4@ zC_aUfHpYYaAHHc{-=k)j1gD zQ+Qy+!Rqf%(2jDxx#JtZNp!YD7uM-f!m_z-Gt%*J@vygY`dAWiu0gx=

hyw`!Jux{Hv%dQPW>WGHTe_aO2akVpt#0#h3cSpT*ofdsxn;7K{DpZecA(Q1ct zv{|7m+Q<2xd-s&Q5d&XXfO8NUTa{5EM$34o~G&zK{Lx{=_CS8RvvG zy0zi>mT2gQ;=U6d+*VlC;L!V%d%(F}ujp0#X44x|1vi!i)X{9;mp5)Dm;tyBg2eBL zg@j8&EKX;#6f#DOkd-B*KzRf&A42Ai0XiomG85aw z2j0DbW}k)GzECaPVrjy%HLOo$FE&g(Pz2F2XJ`3y+kwEtZh}Y z4e8gzI2(bw_>o;8{>At?uP1>rARWNi%Hf2wH$j+wLS>$u7mMsCByiVwA7?|(DMp89 z>j$$hFREw>1WQ@k+MWiwyocOnJq51+{;A#gE++*_TSCFyo0I(- zs1_fx1F*{$XZ70yl%q*GF8gVquc%1$;@M6zQ`9B~gHRvnea-xzNn5*->E(JNSMS>R zs=QJ06g#l?eN{u0n1+UQH`y=6!dz3)9pu%sb8vET^PFbKJ+AR{jQaI6jraohf@V&X zqTj1ao_f?JRkz*yzmq8T!u$+yu~S-7z`z(dUC%yRLzTeyL}fCEPd6(d($8mbH`S)u z2#6g-Tf_v!`d}UKA%FU>z>V*-_FoC3-qU?JBtCiINY>bPJ!M9VO1w_>=95zFBtG*h zTAe`atmiK`-DVe-(`5}!l=1SCxrcev@iRpf3EgQM)y3@Ku>k}I5bxGMb`(J?or%VM zTUfkVuRNaPRE!huEcUruW0-Nm1eu>fDogeltR$oGS2!)TKa-X2uJWG4B)KQ`v2Fe~ z9g+SrGy0u~_+GGY^(2iSavT-_7RVXf;QJ6r>~`X!bgPJ_LG}(}L10U_>Lb2d^{@w* z!ZjH#%jS};riJQ+B>T?COB}<5jHpI2Bm#OH!npR;8cJ&@a8EAsv2+G128=S`>F=d; zUyiftkVKJ@K>5AK1MezMr8WK%bV!7Zc*@}i@!wa>kAaC%id!0E8!LMq{_gta^PR+l zlBA;PJh&IW{Y)$-;h8Q6`w;X+W8jo@cYzlo{i#pHSthJ2PUQYPwAA`S2`A8DmBIt zaq#hv5(IOG{u@a09Yj#te5EcY{^%d!l~(MgqlyO;jgc9L7l!jypWzW4gVfvGHB1yb z)L+4%gG?J<(<#|$^{V92$9F$(O9YbWJ1&EL2yb|(_3@x$n2YUE%eD zw?7$)-zIR*YZB9NLD*oTI!s*Qf1LPt*9m*JT_qX3Iy z-1=+SNjV3jd;|}$U17w*>hI6cdfIc54y7Wu5Z}3BxhVAq8BzRVwqvQJ;iW}MPBXus zgNMTG) zK!I5kqqfR1W!fLUBsPV!4hn9Eu`RKRvr9sEt-^1vrPL7Nq^q|RW`?TdklZR0Qg|tM zHcj!J$ z*jEa0P-{>5Bu{J1ZRkG6m&2EX8Zx1=-4Cm+#YN=c2rhWM<{t}?Tj*|6C?h%HYR7He zTKLeq@;O$r|+b$mCmKGx) zz^R}`OiSH=i&{vwfcVn;u#V@@JqK&H0tMT6m|FmTl^e4|kct8Z=3H~50h37ZZQ?!5^ckR%A%93SDl9isBjTEI60?q zCcx_NC7)`yC&R;*BITT&XFH+<)t&Zk?GJou+%!?ak21vr-io-iX5;BSCBf7nQ7023 zBDvoEnv?On`)cT1UO`19m0aq5c!Ar0uz(2IKe`j-g*}|u27WK}l4RFC;c`>RV1l@^ ze#26XEnXo;6*h&v0^sM1HEnd!M#jHj@bbeuFDJ}H=^ngGL~}rqpX4CDC6n%<4E7-e zVw^x;KWMje>QFfA%OaR3^`)^uKTDp2d)1vHDdv_(8sGW-2cKH^=o#&NU%Fz^Qh|fh zy1rc~*;2bP5K-QQj%-w0?|Gok%w&nW0)HbC1k4f)2EiaiW z*n@llsFgD_7w3s^ut8m$cX>h)~bSYQ+FZH57h8|BuYOa5h)%9FcsAagsh=9z~Ztnv$b~tDS*b{ zraD3p7@gg&j2r!`>Gz3_$Yd;3!|n2A-}i* zN&M<;k3JSZd}l&Zws?s1u^aiWG@qQ4owFUN7eCEKAJ4kHYxtgz>0%Ue$jIA~y;d{6 znVTJTnic|7p7;!RM}lP_jz%Dn`-DV?YhF#WhJby7iCO&vb=-_uJf_zwS7~ITlkc?5 zRXn<=hTEh3c#)EU*q(l9*rLrE4NUlQdi}ows$kB?3d(t#vLBC7c8 zVYrV2eJM^dp-fVX%MOKu(5{Z9@zWZx4{8CfB{KGon$LD1H+Z$b4Q#0>1Td>w$?p?@9SGesY+baB6fAG8Z&S&m$X)ABk zHJmAJrICnOB&`q_x9ERpA3=0P9<2WU_2zH%xijgu=$h<#Q{^CaZ*4Mc^E=nH0>0|0 zL_BE56@p1WxU_y#`fe(Aze5JC(nc~PQ?i~jaOtp^n;?yWyv-A>wG*Z~Bb1GLLTtbl zhfPua0~4%B|BQzz6j{>S^P)FP{lE%D6C*0SO$T$n$jb?ve4rKhXyUPXLpe6qSnKN< z!#m6P&tb+pcahwz-(D2x40*1~qB#}{%hJR?r4L)dzbQBiVD1q_iON^`C& zoZ)VO2ZWwgF80=T5Rlyi0}8HJ!^Z8Mco$=9iVe9@*)1+AjV~Sq)J`nQl*WC`K*~@W z^e@>g)+N@CL@s#qd4kkB%M`nqvo`}Tzg*fcNr>HEo24<)SW)I2#U9-T_V0Q>->=Rd zYrgd9V*ET)lTYk21s(_MN;#47z``$>$2=T?b;QOISZC(tfADlx1XBr2@6}|sywvD& z`IY`f7h~nl*hG*sW4+wSx0mIGfCT(>UD=fv%->^A@5-#Vk?E5gFT612a!a`7Wr$2y zyKrv<$hSpwU^!XXxBxeyBZU2^DjC$eo;3^;ru@`OjQUlr{k5A1tQvNGZ$tGix`rPv zidw4}q5g;qZMy2@{8V{XJ=Tkal~XprQ-mvW&i^yc?$gg)cdeZsn!-#X90UfAAOlws z#={Y0e-})DmFk8Z%rCZ+S%4urVk*c=Sh27mD$(rekRdW6(5(@73>FXlfKT~TmfP#w z(*-pA*NB!XW9PEQG8KA|FD?seUIvMa$Ou!wfUDQp0z~$Vp_W#5t`^3B&KXQEKI1T= z=(p6B8rM<-f{UFT8gF3J3P41N%id?@p2Wl0C0?ptqZs z{_@v1(a|5v94=qjY=t3)|KD63!3Os|a&Z0=Y_qEIg^9l?zi5Ofgbd3{hg6N?|5!29 z=Q#B2kY2hb3-f!x&zO5d%}4{^;%ECP;i9X>jKBOFDjxR@>r<#0ykSlBzcvl_A?TZ6 zk~`Ge$q*h^hkvSFnyyjH(66jFsr*Xwc6 zKD&~FeF*PDFgyurb0p+p6;o#v@`0}kvcEEV_y`iP36*M=U1>GUk3M|m6!|hlK6b!q z_zGI+bDfR5!nmSBwFZGVn!JqVR}7J=p5Lg#{3xOu{Lv2<3`PG#8+`gB;Nj%K>hA^o zjGs}tdi*(tNt;I==)A7gcKP~G0!ikg&KG24zpQcB0}qFvq{7ZJ)j3+v09H=M!<OzCssdqmH+amGk0FnU|<0Q*sNlI9@l{w|3%SH!?c*u!C-mX9UN0C_(Ux=8Wa@}Ft_9J6q?HjDo%laZGENUgL=e2I)@kVWnw$+xSk=yRIyo={f1*?3hXt?i$|GRC0J0wUR-9 z;>=nfin^q@a>W!IPkY8U2X!k9B~s^Yz9UbZV47qK`V3GZ5Jfk?D~FYpgBvf0sWXVa znOVU^HwXv-(S1f=d-UF&#(LgLzMu#*3JJkHT^!vAkB!2g3?xFbgX2{!KZa6y|(T|Vv= zMIUEHc^QO+*6(T+A95;0W=)7B-8BObho2~M)MA0x+JsHA`U6qlTNOX=eXe6=ytbi+ z9X4h3G>UNv!1iB0z((~u23>=q?@P|LLT}!u=Ov|CTvjk0wCU=#p4Y)YI5qMnsI4+I(8ICh!WbRkQh>f5+ZKP?Mj@dg=HkS7V%-zt$NUiTbF0hr?5$E0 zeaSIX_CKz>OaDBv&MwBj(I>In)0@cF9Jt)EoH%oy{bjN>Djz4Wac;@EewLGsL<=^k zf~0CW$(^`gxNa_L(`pyQ(j_Eoi)EMk^{Ir6o@})4&ch|dkQlR<`1IsOZ&u)bNWSMh>_3uwMet_V} zgd3sG@`vHkO?_>7l9%Ji&Y?twT4A16thN91(J}C}mufy%3JWNUNO4BJo;|2K7?@bK z<4dCE#&10qnP>LsaYd#$y$S4|3k8yI+}T?`Dwj zB9sd4o1;(>!Yc}vw}Ci0dB7SNoX}cgw)q-gtLcRDc7jv3G&Q!{pn{pa##y0q{f%SU zn+uXE#?~$n6Hpup1@-wBr=f2QT`DvU0gHnr>NxY%FXUfT$}mbKW7m>3yLe~REzP$> zrHvddAg8?fEw2Y-9xw?ALOHrTrYxM2L@7JPmJe8EJse&KN_}6J_k=n_jIHflP6NiW z+%8P2j~2zR&#@o){ysR6P&-H>R~&y{r4sNPNAPNdf;lMJbg_ZmsgpmauWq2O`_1$l z+r>HD?B=#yv^`97db}16i+d&6pIclL!=-Kb`S?$rAs%lSRK_3MA2H2CKpg=s^n(V-S~z zApGVXp^XYl!fN+-h9PdIsMZ{1H>>mK)mMMYU4S?_*eYGQWe+j4ak00881nHQe>`FJ zqlJL;4UYhUUZT&3k;^>&&!>fW*U5-_P}8aNUK3{s2FeQQ}3eNas{A9t}BP5`6U z#^GJ&#C(j;Bw=!%E{+zkdW{p7eqY#Xh`A)Y!*G7KHN>Ey@&|i;LGSyN44nLJJRA9V zvv4UBsL5#}gPUwPHM5?+0|={{%sq;Vy;HW~$~$nSNFVuhE*Z`<$H?1EK+NQS+p zTtCXeJ_L2r1nLS}hQQu)e{ulNSg{z$Q>@2uiEG?b4Y}#Z5c(lDgZxv)^QwH!$P+9- zTNwoyd@Wbm8X?aU%CDK1xu@|=%|BSeT2C^Kzj7p$$I4-fl144sL+thbCw`8B7T%)}ARy@lmLbE~-dqF1e7$}r5 z2Nlx3aK(Avsa^87WEW<(3e-Mius5lvbr5aVjpcrH8`p5#*;Fl5u%-Hes@D>Pe4#N?5~Oi+n1v z-zl$#Cqib~f_h*l2J@Ku5llx6fYslhr^q{bJJY`ytFv}_uIch^S>4zlVr_Ac*rSgUA^=%NzV;`%lkbN%S?=*`85poA=F8^*jgOj zH_-C-j3>!YG%DBO{ioSqb3wYfTIseOq?_a@@o5dIZxbSn&8?vC=i60(2?(<`W?S^A z`IrwodzwS+JRrxziD4yzXY`wd-MIMrLr@A~o}@~v1;4i4e!ei&KcrDicB5kI zq3BdUF$C+jdcVB(i*N0wF8o0>tQjr0adj4kN_%e9U3qNG0SnUMATV$O7MzEF0xP|I zrLd3;xsQ~tg8u5LV}cH4an>Wve<1Fbf9x{D~#!VC-TIngv2lz!&Qb@=Xxqz-OGp6oXkPZb4nZ^{Re@ zu-3OQO=Vh1xv#p)L49rw(}%e$p!Am4mkR`!*kPoO0r1MI;Z#_wwkDA z_OLiG!T*nq6X0N$)4yev>vvg9ez@vHqcvFjwvb@G75N|&?_$XJm07N;g-<^{iUBG3 zDf{2rnt^=?-tyDFXt0|IWIFGe-I<}`7^5-rUigIvxu3g+FSbc?C(-rezNu#}^UlWJ z?57KBmDh@GC^RO#d{1j$#D&RhVZD0pD*YI;+Ru5p3nDBq3lIC0{t;A1EP&PDU&u%l z{2zP_9!SLNjF$dORxE|zkSE>k*Yt$R>YRFgan`}Z;ZV)aZK+fm=6x3!)V}^poKSA+ zP^{!q-nduv^5ytQ)E;2lp$GIo~EhFp2JS3X3 z5o&9l4X$Snw--%b8msd=C|ZIvC^M+7iHFr`T-5z=f{iYZW((zA6YGbK&o>D1m*{s~ zdwREAA0>Wy`p7w0#)ga6&g?WOT4Y^z;qbDR0a8gY}OxFU_U| zR%L7~te_s2C%WF%+26ESuYP$Y2yTp)2s93n#aFU*%B1LhVe*(|%ToLpENck?{lUyn z+Xp)GONvtEJdcNnD8Hv;-HSQQPf3*4ojrfmOX(*oPIjuQ8L&ZIfo5$AW?BOy>x5Ks zJdecWz1gVcx-rk+)P*FAewh!n+*CdpkvGT=E>uW`%Ab_ZpZw1MR{5_0(-5I%P|)}6 zFu;?rV3U!A%g1$JXSQF{S zqjOFB+g}>}JUUApC4H;8^=VM){p+g1;{nP9IX?n{+RJuom9$@5UG1ayTdu!@6f)U; z4g1x3@7T2%=kvldYujNr(?18wnmfYGzSFK}L3bm(3#Zecgpd#VihxX5ji2O#m z*Vs;Awk(%)czn!SD9nE6B(&T;-xA-)&QvC16ee(E(O==t$HuP$s@;xj;HaMlWo(Th z_RioBf`Y%r;v_-s?0%O>&9;n})0EZZ{G<~T-3e<~e7aW9nHK9Ogne#LGNlSne!7#P ziY?GJ8SA=f{d9@4)zQ1T#(P1@#Y>Hb(O~<_dBF9B|KtJuAK0rBc!FVUfYsmM%>6sw zk~7_mF`;pTTNEmKsJZuE>yr{QKdSa`k3_%kB6|#3N*9`94oY1vc@9o*jir7k5z&=EE3u0kx4ly%3QYJXhJbX5Iky^|arQbn`;w}2J!b_!T`wQ}z z0+)y<%(vPhMTd>yUp_ikTK_P`3on;3`h0(Zli^{A-Ve`2Ium@Sz*O{pn9#?xJ)#ai zA88aY2Egj?VKU`aA|a57181$83AGNM&Yq-}}u>x~Arc*lF~L1^V@qUo1*zym@)-=7Q!L#Qp(24T*@8S{R^^?G!Qap%9b z&;>Do4*6KBHVfym$64M{XLjexk5|#o>m)sDSQvAy&4?xR`BtYdbfCGnDr$!HoT!wQ zCo@GK=J>=#*^|vNgjxfB6^2ijbP^q)XIXd^NdCO zzD`c6@(wTL)7;8GT*2w<@wa%QFTN8N#H~j3`4jUM+~{Y-{%BKWsoD6H(`iOQDvZIe z-}Q>sT)lXaa^!jSqYto22Pm#aN6;LR2CKh6N&aJa8?v=Kl`YL!>RKU!B%#4 zhFUc5O}nz zJVWaF;^&9(agCAz-zy9TiUq&gs;VDt@>KT2&Jg+^+Thcl%LXS8R(}ss6kd~c^7ZiC zf=i#GQ?yE^#mDFkrrh<><5X$c8FJ>XfrrB(db+XF>I8Y&Rq;eauS6ih-iOL)AnC>J z*&PMO;hV8oNnjs>t?X#$1a-9l#`4i20@Cx0D20f>Qv6s!t65EIi-M;<`{kD}kYEz?Z2!cQzQ06ZKHl#(Aw_x?{` zegCqK?8vj<(-K+AoQF9l`}8Xs3(bDp`9 zKMIwhj@Uujt6PL}*SOohzo%QbTR>1N68EX!%_i+S@2S zgg4yRTK0p%v;TnB9UH-qLzEv)Q2F&X`Zgu#;8r?+f%8qRobdzX--@a9U>}0z>}c_S zZwuk)GyLPBhYyZfB1)o?IiE|(VJ^=3eU44;l5^O^A^ymDvrG!LN;s6%!)2EW+P_kb zYJnqU$(Av7jxtZ?{M5wq4 ziA1YyVJUpeo_7^E2FFLiK26)4a+>}<|NqC@TR>&CeE-9Aha!lG0wN+IT>>hgbT^39 zLpMmLNH@|UAtl`{2nf<05=u*>f*_%&@XmRp?tk62zWluJTDmx+rlUv$Pxv!^}p4N02GhtWEU(bIF4 z?Thu|m=80l_ms7UX0n-y6X;dTvC2mU%I2)9x&(O>JcGAf3AiLaJ53_wciCw=e1(Vh z|DpX4h}gjB?>8NYgfFJp=3njI>Er$oYn0nNm(TWjm{UVJuN93NOB-51@J-j%^}rKT zJ!yI~GvF2Ni>vS0g{QU=hUK4bOY4($#|$5Ud8ecksODn@(=|FS%nE=yqaNV%%S}{+ z`}*)I7Xf+jnD@*VY7<>C>nFiX3vX^wELK{|um2KD%AP=w-;ZY^qmN^_l5t5SADOxT zC#v0@B(K?zP)G*mF?le6))Lrg5C>rN_dq=g)9@E>RYNc?y3D{H)*Q8EuA7<>CW=XA z{P6Otd>rWIaG;7^FF!9^ACJZ=V%2Paim&o0=pK4e3}rRKCoF`$z-1^#`1Jjl%@G7i z*%-t9=#Xg^wD>#Ydh*?UC3EGS{7?eD3>)Wut>t{{QhQ^oY(rlQ4JRHS<5eSt59Ra1 zuP9PYJqYFblkLst9?`^@u_P^*_v*^rly!sz6PEcgeP{%rI%ENi{vN6WmY2ig3g#8I zu6A$z~3&4%&)L^7=?csU%Z{T3}-`_^zj1VYU_i}@XmkMPklZ%T=| z3)<<&zfPkn0`pFlIndL$a5^jy1(ytG%T4##AMh%|i`RWMeaSd+68(-cQ<}_{kwWWi zh_cyA+kxEy7xfoH$+qEJoiuL;K7|$FakE^FU*Y01ycO(c%pO_-p@0e}4Icjgl1p#f zBKufp)kH=w5%bnwNKs!8J9ysvFifkGeC~PJtuaUsfdi!da@6CBYVo(A-XgxtJ_@@h zbiQ9_AJSyBhHcC@ps`B=fw>3wQThs+fmoH}E3L?qwq%;pb37e`FAF}N+}GEsngRqS z&V|1%^l(RaEtnL78S7Y{+<;BZMF=g5<%+BP)SfD?>=Y%Snl^IpTGnak_`jJ}V%v}b z(T+BD7Wz=t5@S=#limZPG#u|ZGm)y!QyDB0^u9txq5GAj`?QzP!Sk-{H43XpZxK*0 zPEW_u@tCuOQrj=pD~+5xWgBF+)&Ua1b3&--vw}$7*X7ihd7}Kp^{g#HoqL_r?PrNO zn{Ve9jR`-YE5(qw+jJ$8E;zg(E;C+DwNE=sqQTl%$k+nd?vH8o2l0sV&(7*~yf+u= z$QEMr;P)Oo;F?k{7o{}m+e7RT1{RIeJSnuinu=qxryig9nJ{&EBw@!+O#FP6pp#vT zZc2bHwTP&R5xj@)Nnv0{(M?06ua&o4#yqW?nIqp-{b0MhJ9_)>pz}_b)jWlOx1bJ4 zc|B%m+FbG1`iBQx*^jpTV9!4Pv`RG>Or2WjzEMzs_qcHNm#~GgA@KSh&tKM!!$TR) z&@sW}`!aL2)hy{!Nzux)I;&UWj9mLcvSFSQrl1wP0dT@OpDMDALYMN*2d!QAyg=Y% zQG#Xu%+Y6*mrZti_{?fPl&`s@`wKgoz)WpU>bv)-^qt3hiPc0vZkDa(sbcQ|zl|#SjL7-$?YwhLYlO)-c-T0eETALyqFi407!6Jn zbpZcs8w0ZwUtd_BP*d%TFxsgKKiOR5_*;A&S_NB7H#4tuu{>NI^XHa@f@y&*0;-B* zr3*B*V+`t~W4LEHzFJY==p}3E6h{xe>eXcLwyJn>_(P4Mj|GXRq>Yi2F3gynmDTK2 z(~T_D7eS8KK3*SuSrPEc^iF&GSY0XR?bj)|VMfl+?`s?SgBtlKL>AmZXC&-^bLB0b zswg$8s2Uf7+sWv4=Avk(UuEfs<$<;i;Nq`uW?*v?RdK1uT4O35$v}Vf%E1TYC`S$X z-&JH*9@cc({JFjxZ$iYK^i6=%n~m*Mi()Co*Yv}K70bw))iYK-IBordcx4_*XfbYN zRBplj#nqDLc6!FPAZZ!2Enq!{U_rKFxH^~aQPr~E(De$iR%QLYm%VI@nlyjMe0j4~&=Jg*PgpQd0Op<2UYyvCY;6v6TcH{s zXS_9da$sQ4Cg$916DJ>K6#KXx|M(oDZL#bn(#A=}1||mVg*RBX-JH2dAs08r?A1y- z>n)r5^0%J`GdCqrYbZT({uAO2r+p|$fh86q4@Q4~(yZ?~c45W}xK@5h^10>GJ>p?y z8HK_~S#;}%qOak@d!9!SU8v>aVt?{ctR~7|2nCOu2^IZ{0NYZb;dCmcv)5ob_b{yNjAyaiz{Q&^3f{bp^-67+gA_kQ1ePTBAbE~3ai30Ir$^sI)d*@*!&eO-YLA#d)l{~q7UjzuaaKu z$x#1j7CItKfl~{mtDLeUnp;~~>M-k_7{|_l<20`_%iR!t_wbQyq^QmBdkHT^m$hrTTaRE$shY>iRnFeIL_D^i zhS5w^_hDqxG7>{`L444j%ZN!2%saU}=o;xA6~O*e&Fc6={?E!NK9pLom%sQsso11Q z#dY5rz-3b|Yb4vO8`|G^G9bW!H%ey_f2ERSqb`WXApYalaBqH+S1EV@?m$JVTei() zFeHeO0r?Y%AP&F?`inEO@h|J?ymeoKnD!f^-!H9h=W#!Dl4=%AY#R9Ro2}8B*F}Rl z8oVC99m$#VzD=;Szxwt?eOkiKocBlfEC;$?Cix}JV2!8*bbi3e=}b$|c+u9{PS+M3 zoTst}&ln33@c4aJ3RWjy-HT7r*I%K1X?vYna{;z|C zn@>Q?H^dw-fp`4gzQXn}8#~hz-SyWnuly(rCO>0|@DNFy_VaRgl6xOB2_-L%biJvy z4-S<)N$xwP%K82%yzvfu?uW*=>q|@#RCUEyF6Av0XOfpTi>d3Cdu}L!tEaw&ErS(M z0d!#&3_v3SE#{7o^wfomu@%4S?0TB>+T9AM-eQoejNQN@`|+CerhCTZ-P798LZFvja3Asa1kqA3BXbx72=O`oM;Dy0 znhV{FZwg)}8k&;I`-)b+xp5FQ=l*U`E&*ZSv$LJG-FjWL zJYD_t=%Z$Ylc=LE1?Se6R7>}c@P2fOGemS~!%W6+zH!%Dip>ms)Kgg?Fg;L)2c%@# zSOM>$q219&U^#mh$T|lGP0Yyq1QB_HHnEZgb?#)W2PrWU%o_bw2F9i*r;V}Db?f5WuYSmwn38ykyn&{HAX6QgV_y2MM{_JosK#YLV-zz!d?U+IOZJ#tr zGCl;}SY~wj-GQLX-Y4MwnYJR9Ua4{C2)6stEbi9Yr5|FM)bAMwV$C9JqNTl7dfgBc z_qk;B{gaDe-YGp$*U$viEQ(BN8-O-W{ zA-2Aj?XYH6J-m!W_WsAKCq9x;-2AEdeS3KL5a18|I%D?7laDH-t2lH+$2Jh<#(zGBv07G>L6g5{4oUwfXz}QJhiy;t2%#b)Adatn$f1=<2g<#n-+a(rcaEn0f_7d3P_w^MqF$J;Brvh%5jd}4(wYy$V z(SM~1uqUto&b7^!y|zugqDzG6F8Ik`#N5&d1}ak>%MK)tm$sK~YstE7efvIrg+kXo z$)7<1{|h@T%whd{Q2n+vxOr-USYt~q=y*GkhW8#?>_&1TtB4A@qg*c?2^uf1>|WY? z8+YD_XO`JA=)JcfloxVL=6@x#NLUz~!&r3ARz$j7 za3zALu!AuWN+-q^x$lY@JyQlhv!9?jMy}?fu;ohQ*dL;z zL|DE>+#%Ri*V!@guLuzMg{S{}DkxMY6y;ep6rBoJIbM3-5_Qmi_KW&wd8MCmsKm!4 zqhbT3-*ISbw{L0LYPN&#e#*zoZpO)B?P$$x>}Yxz%ld!t&-vfX&XdnIXT=kgdQL_b zuKlC9%;r)zjf4|+MfioiFA~^Sd&WyWQ3Qx6`aPI6Yu<&fVt%iVs2i_H-lM?Xoot3= z2hM2@!1#}l{qJ7747;vRHlM~XG8WFu+-lY6mwedpyASs>yZMn-eTPYyDBV02+AMAr?_5Ml`?_<0&gguP zUVSLn`RQIWag#X`mi%%YnnP_jk_tI)o?aU#3aApPJ$RNTAUk zvHm47FCS`j&&K>C+HQ+<@w`&-uKVt?s9qrHOVX(3a~EDX$3C(Xe0%%$E_Jzz_d5k* zd3rPY`*v`UAj*IjXeAvI2cy3~i=N}!FLw%e*q#^KmvGpiN8Ga-Uods(roGs{sh^&K zo&#PEhv$D)CIs_N-5rk#8$t&*IIaH=F(^v3LkXE3PgO<65IH`L-E~e9OR6Rj5~dM~ zz<$-{MCmIQ>zgU|sH#P#)6Jl>?cN1V=3Shfb#hLt5(_%z1v4DL2gVU~@cd`P!5ILf zzlSN}cSn)GMGf{X)zX3$;UWCyhgHbaU%v)taVbLD3 zz|_Wky(eS!%f0=pc|p%OZj~;Ae4f+tNzd327}Cr@>ZSp_jKLqM&bXTVMRKWHEtMOK zZaK0MOsZX&>~AwvlnhwpIoCmQO}%ecS5ldk9#j27^pn{y9eW7}R>3z(A3qw*rEAgd zQ#3(0#0Na%oaW#kaQ!zWFvEBLGkGjQgyJ&Z#|@tJ&BX70!sqf@9}NjIF*Xdkl^?;C zQTf$9OS(;}mFu|%Z&e}lenm;~YlgnW5xqDOO0FmkFz=LxV*t<12kx=kU`d_Lq4FfT zF_Muk*exoTFA_$*)S1bBApY$ps!y1~0~edyVufxGlj5x+*_5}1(5GHjU(0I=V}4~H zm`+Jw6yQcE9l#8Mgk)fS2+bjBF#7wIgf*!W&HljD6UIfYardQQmjq_6{A>06HM5+X z%`D5Z%i!hkl|;%S6J>MoUEGM8@@wS`|Vov{*z$99C26~j1oBcDejZR>|77tcH|T7yPrBxi>J$8f^YZ8K4(_{h)Vjo!34 z2jB7%T5!ZW?J)_@;(A-iFhn;`QC~SFBc2<+qQG~MjxEqe9mPw zK2(v#ejBzX3+A1=Wf|GQ96*8*{F(zr$eb}++_MFNWof_Gp&oZI$BN=Rw$OLsbkDL| z;_MwJ8HzbI7&pv_0-iP#>}5nv6HLrR&c2zfd1>oyyDGyjxnt#Gn+9D;(f-grgb2`v z$b-?}ucKCrsUMkNDf-hTjut$d9J;BW zSyp>!+@^T>u65R@s&@YFfx!0VUGs#u&`po?X zGH!+x(|TWLvQ<)%E;1*Cl37sUq`|}CMuVt>(cc5)T41*I>!4sY#Z&r2>0`X=58j1A z2d#UKikd;`nv=gY!OP(Q4ao*`j)pL1G7{>$$NVtvet-Ly)^5N8a~EQ)8N#dY!+?jA zFfU_An4S^L_OQh*013nnfpv7OZX0K}i5km?>Ym3P3DmIo+4J@O18g~!rpxWqC}qS6 zG|Z|&nT`yc_PO~c6pyWI2`f|iDH(VDJ<3DqUG3g8lS|O0neM7Acv)Tu55#FMir%YF z^z*JyR?c*`^=J|Ey}sar1=z+sX5s`ch>t_K&dkie=-fDJ-Vc2uw2UrbRZg)qap1*9 zkbKrB@X+LJXm^$)YKFpr;h?G9Pv1aMPA;uas=>61_c!>giTIPbu%0{rdM}@Zg3bT4 zstgRcnH?eF1PAQQXB?hb2tuHZ(0b_a>9W;bGLtI|Tr0CdGDxF%kIoD7%+kS{FQW{{ zhvRWFrs0na)NyRSR3E96xu)5t+;(Ax@FFCfP~oIc!}K4|xX0w@I9LVmF8ebrq?&0J zvZ#MAuA1TSxb?eOKL?WrZgzqLRH?5#qRxz}M(|ysuU|ThKg3_%GL)^i(BiYGON3&) z5KvCoy#&_z{X8Y1j=L6yAa~cs=EQ8gO%k;umMN{P?myv-rXLu8(Q)7N3=4LY3_8rz41vECN@bjePZKh)>cKalSJ5J!_4vS0KR0WH{VEv-*NDlVJsr)52G zB=pm~$wIOzKXMh3+u&xdCACS>suGNAq`>q8v9hDZvs+^jQVT*uPUlj~iwFB-uj_m?LH55Libf%glYJpaQo~xg;pXD6zOa;h zZ&6!N_{|h%e_|LOeP#cqXBx>b0_ppUadRS{aXuQ{pLe_F?tL}u=21P@CJ6&^JVqc= zK*!Gcr2Tq-h=gQqjAXOPw9d8rSft8$3TS+f9JRLz+W9ugzG+l>N?Tak!oY7MeQP_L zQzrHMFT_jYl(P#tu*-^^&gZAKJBDCxD?Xj!pOW_&_UR$^76Uox<~G*AlYb1t5H6J) z)9t!gj=O%(9n|Usl1q1rMEIvJQPS2VW-X&}idpNMvO=jMC%ECgeo}eOH6AyD>_*Bx zT06BqPo(Tah zOM@kUN~{|?fn;j10U@6+_;p6@z$1U)F{MA+58Cyz1mrA3u}mkY(Ka+G*6MlYu8*O& ziYG>5Ti*oeCTBKG7a}QlDKoO^iw4eq65O!9^wr~?p%HZJMTL|8vjLB&gVEoM_3!a* zV%C*EtX~v*dsv4yiqKCNUu{T>7T@9w7Y_}uJ$7;3Sr4jYy2+?YZF#$LIFlaxO@4ZX zQ5IqcyU55Te@qT2Ci9g39RZIkmOQ%Uv-0st)tRVT ze4knQCMC~x3s)Ub|FO+U^Dx}%ysACH}$MkG> zU3cL~B6`8g0l8hv^DR^2m@q`Prl;czcMo%N2 z_%RKXU3gI>_^tj3oP!1=3gNd#thmZO1B1T>>xR-X`d=heh-miocr+&72>~C7_&Bh` z9KaR5n8G`Lz9mr$EP@N)~Gr$;pj<{eKpbd+34zO$BH{KU>@P(zw>ekA&>$P zJ7Dzp>nkRyV!_7bv8&~I3i{x;H#VI|8n;c)9~9jcBdH?HNK6GUhp#WZOU?XYX;-eM z#fy~%zIvM_@U8wqKJ@@;Pk=h??H0Vm0i2m_*z)ptnPUz9~B7!QHm0+dv(S#=~pI=5)U!( zayUr;D>xI(JF$nGoJ4m5kq(X>V7EW(q33I7x=KKxc=_WyHV;7ttT#k;2n@3a)@`P* zsoDJ0?MQF?hs?~-Nj6YkpA+L@&*Ez!f2dcIet-5IrVT8&&sPi5Ie~6-^A~<3$2DZ@GjcEg!VfeDmf{aA4#%y1Y%F&}XQZ$z;E2M&g%yV_Maz!(d zoV(*5t764v3>8KtnJZL+WY@@cdisBS+OL#7=gVJG)Hp^!g!?%6`D~sW)({$C0T~bm zKaSVYf2{nd_%e;@K@`f>uJvow!g8&z#JFNu^Iu(D9Wv2uZv5Hocm&RufviWWUg=l9 zNbgh3R(Dfp8{)R)G(@UIT*t5a>O)onKG2kxz!bkPC~#w`14<^J=-AN)&-Ge=ZgHZ< zr#TfUZ3osgkua&>DxZH^FZnGLCzsAs!2qUfYiDPz1L_x<83WA|WbhyBf*h$vPk#`l z-D%KvHe_+6&i>Fhx^J16*dKf~+56iKDVV>ojx~sJIq@oXy^A(ZViQK0V3N8j5Qlv|34lIf5o0 z?Rkqc>c^X4-YK!GW5Wh^3A|V;+2w0u(; z|Iz=Rzo|OQV?ArJ&hhmth*BH-L3rV!EPQPyCj&voKN$(X#K8d6C6`Fd9BRN&KEFK$7z48&)>d1bro!)&<_ zLMRYr@bix}ffEO#zlSGYsTr@y$*H2p{-Hf}8^*UX^Dh`ASCUo;>C{BbsZkQa%i-`0 ze4Xdzy6K(05QpMyFsw~)Y;CmvMH#8DH}1Y`&|E|q;Nesp9n8uKq))({^c_K607&S9 zFUfl5N`wJv$Qx#AEW8OBJ2b{u3uj|Xg*VYQrzsxDD30-GQQqSV?nG}FDxC8BEW|CZ z0_*Ge(P8^&hsrcXn{0E5{;C>uFya1#g+ss&x%f|jaXu^S4*!PrXkELBE#J;cWNp%E zAC<(W>7Bd{!Tv}fFn9a`OiNG8tq!R}Eb<3g@A`nlplAS1-An!*ffQ#`|4{MvDlqTV zmBR>T4O;%1{@-&1z4N~(Irm^_US|GPnVi zN>8^kGvqY-P!mOL*fl&+iRi-aS8VAOz;jPAj;J5P^vBo-_zE`ET4~HVz`w5g7X}LmSQC`jfcSi(RTWXakZGhxh;>ZT_HY8DQkLIn2;)e29T?K4OQ^pA0 zKLt7_gvYD;yUOsakf5=1es{}s%`)Y7cKtB1cy72=<<5`U1J$au(q$|Gm%yetf^>di zfk!UnF|_f>UhYd#_3U5yVk|QrLzjHCKOCF}>Nw#3%$)p-HF9)Q;k^_M^Q1#{X+boi zNnO^P6}ds1qDu@EcoIM5;*awxFL>yu<0)D=#YO14th}arT-<2;f=vqB6Surd0C#o< z3aU7@szC8COB-8wzyzcko_z;;cXKQwtp+jN+@&$fzg&X8%`4c(aYOur@NmfNgf4nE zryQXWzI$t(im}?}ow${&^igk;C=X~?$>!B!4849LLXrx!`~5@x4_HSGfYINts$79i zO7BS+H@uB539MY=_Lgg*)!o`yc@Q%>=A5$w7D<~B=duMXvXj!I}r zVIGqQ14yhNasWnu4;1zLln7(-cdXXzW`f1gv?zPBRYyZgeDQUK!}9e)eXfM<_}?$aEAd8agJoj<+p0fV3c))|AM-KFFA z?z4nBY`>#C(B9RVtiID5Vkyb$L5^xu`=rf8mUv0mRx7WNR1qybE=7BC zE1w9xc1kyb72*H^84w0P4#9zFgVEoE)7>dUp%?*krw(^Ab9SS2F<3oEta{b2pJcG+ zllBw`WafZ_Kz;B>44wq62J>R%vVyM3mlx@2V_@DX!y2d^aMK$r7S+ zDf9P>JKmo1Qy#M=n%{@}=QTWjqHo#$SZeC1zV2W~Pt9BdDFxW482ATR|Lv^PqWh>Z z^J|MT;_-Ld2MaN^y!elDV^{C@K1|p(l>A7kbp)1A(9bNNy4PrFG%T<9q{^Cz>{{f0 zZO1@fe;c9byJr-4%uC&PwleCvLH`-aU&6a7i=?f3i{b)9l8J2IaZRQSy@XA_~QM2MdsYIn6 z7|VW&UKM=J#A!O^{%phhZ3CnTV4cv1Mkg$Q(ceS$@>8jyn(E~n^t#PF0`n(Au zs^#Ti2$-@a`5F7sqvgd4T z&*Yv^^_ouJL#9sSyQ6ty@Gis3(u4{X?QGt>!}1Yi!ZPutqJDUICm;Q! zw4;Z_7sEJ4F_^vu)KA#r#8_m)ho-c)HJo~#LVwK2+;5wvsgZ}xJs|Ycq-RVg?^uqf zsD(c0KWuy|1v-}E;`ywEC&d(epKFDjNp0PJnk`kDCwWSKcIrVTA*c5iHgmFYynRB$ z%1DK!6~*N#!B2FmD^sF9W{&A;&RI6KS?T?Ls5Z8V5+J1pIDpN7%jd)>)k8OmxlBPc z?&n{wcpq`3Vx4UfUrNp+QtU30EC0vK7mmPc?of}Non+9AOKU(YvwqFb&9-O7gSW$} zVu2>0qje{-S}=I}mi5~5&~I&Jr?KWo{UAfn)=pgS$U1_DOPrWz=gxljvZ#A7 zadW&6q30umTU8#Nonhqi>zi$KfQOU&oteEpICfwb=9cz<$}j;{2E^r_QPcEKElNb> zP47g(sJu0o^@8zpV_l`Qh#9KNw<@{!uYX^q*BK+T*rC2|jk z(&w3RSIWsENEydD&A~BTa3=l}TmiHmp_@g@ub8i@=rF$@Rz!JmA6fG4-8v$p47|%O z%{xbM+2_s2){W6fJ|-o?d4cHsk)2ejhtjm4Z^P8Dr-M#=2FyEUsslv}j6schAf_M# z3usJnMlt1870Y4mgg>=nEdGm)9LbryF1^7UzoM$uHmQs9$6k)sqe&6e+Z-Jf->#i+ zd&#g+)WqV}dQePj`=+9Ob?T*%jr_)o9CC zoGyWRr$7RBR%1|(90CbkTtM5IGcG4DXEI?MH0ge1lIoj6-g^u8#PZ(a8E)TaF4mfu z=l1cK)#p5aH^`aTT0$vyxa^ zl}ibmG8J-oF(nIT+TP1A4C^1Yfuuh43?Oynu!3-EF{NbfBg5oI1Ca761S1+WBI z$XX{TCRgMH*O`Q_HAyS~vH(1s(#(NvSqBD6@|s)1i^TnD(tSpK(=QO)pI5FW`k4#c=Zv6HJlN1!eL99SpWKLHk{fbO-p&;_=?r3_9B(`tJfq=vglI71C6{ac5 z+Nbn-fil>4kJr|y2w@<>vn|4W`iushw=XG!HT{Dc_Yk&9Uv_9+Gq!2+@i z;^9AmrS*g!gEaeAPxU@|aI&DYdh!_YI7bt^=jI2 z`947lWub+^?i`G?&Jv^6BDVnR0}_Xq06D*prx36V>6|#xzL_WxKSYp{iq?y`Zi=mH z!l$%&?>_gl(RYPBk6v*k(RqrSS(;hu*>SV5aUQ$Ts3VheO1^J8^^=g&b|_pFA%HO4uxl2gG8fC^nC74%i88R_JdE zn;JvWbjL5RuGEU|WoQb$iDa}BB80I>Ix=McfXWD4)86=jul1)CNh?sG4^&dL0R2o( z{vRxvziYK;23mryqzX&=hf5SPETlB9c~V|Xk-GBE@OP37R7BbaByxgVovodL!3jBx z-~5^yj(b-!(e8EZ2xfE4D&ggcks4$zUK$#xvfuU2zKo6^b&-njCf2uR z_SomBe&w=K|8Tn;v-Q8cN8W;twy=}(P8kt{Sj5vA8;j3uR~la@&>j5jzgjD*GA-Zf zT=lDx{hf@SvA!YDXYHIprrGJjR^u2FyLY1--jO`wTdh%|wNSsB?UDldgMPD7^xSMBJc|BddZZ4QJC~cjbK01896+k-|K#d$ z;~p~hpEj;X73|%X<|b@~ni=KYQjo-D9YN8Cb0}g4f;yrXE>=~5m&2u;4(~wRT*CVw z^FORiJ5W5X`1b9_Q!dkVyQ;Cw(c#zrTp%L$)SS}mDkgp^ex3&w=P!nc}y z?*^x>%#YI`cQ#XB*#M=cY>agDjbQdCyU}guXG4^fRJ}?M>Wh{3(m-5{w(5j)g2}g7 z?)P`4{kkS;rf+KmGc&aNM~#WrX)76*>t9hOUHf8c@H?&Lp1QM|r;%u{Kl9|Eel>r^ zQ_$4H?j#?1W<^e;DR3Y$1mR{=LW2+th;vSk*#QJOx1sHvq92qLyVsR2)UGh~bAVlctO z8wm;r$X#V$9xJ@Qaq*!n>}Ba^1>P%`;x>=%KsFIOH`_^vEjnX!OY%g+$(oya#@B?o z26^KeBCs3YhmvVG7LVZ1<9iB&(sI`NC$+|eNUBI;9u`La8XU$|?nn^FpXFFFqHpzH zvuBu&<(=*E6ft!$2E9U0zRA`vEtR(H#`ae-O6aad&-r?T)2iniq}ZiamPcRtZOk04 z6GT(?&W!R0^p$v~zR_IT7r*_qQsm>4diFyjl0LFAMjOmKW#Tiiwsihe_!1n~P>9pn zVhuf+O$Ln=5{gYWJ8|NSKGv{XjosV6S9h^k!EjeuJC5#sY1ZQB#CLr+V9&_(c~#L`sC=vGzM#r2Pp_@75H@1M(t!bJq=B#OmJ0$J`N5v zRSfwtQML7&YR04`Z{vSOa8Ob^M}AW(O_iq}`$KybUujOYQ}W}XuaeQdd0|4;>W0-1 zWG)E2%-keah3X+;9y4(W*C8K=aGjZ%e{qjKqq^nT{7fm8|E4v5=zstLi6%*%Tla>w z$l%wsr!z6FfCOCrOoggNVI_aKj})^uBKodY=#9BZCaMApr6%Rqa|jq zi0UgZQQGLj?_T1pjb0gJ8Iud?l+5_eXr{NI^@C-8OdlHkxgUbYz|3Iu_v@=ce233L zM}{AhyE=?FRl$ zZrl#lpGdq#Bx|I&9K}=vesv+pTVWV&F!S#%hJDo|^-5wP7L}XkwwRpdq(a_sAt?DZ zQg4P@?xwHw<9uqCxcZiCd2b=MxixxK#^x41ga-N{3x`l0a&ZXNnOXUlwKeq6_JvPu zIQ;|i6np&U(;OkQMd%7}@@eWFx*>xgh3JpYkf zAW(?;zs2~{yP*3_qaoFZu%WGJhUy$pesbF|e?{Y;GHGFd{X>fJ5lrb02r^e}@%%6! z`P*j=l4 z%~+B;@^3aj(8bDTT1jh8Q2ZRHxjVdaP^X00CCG5$!T@b&3{qL$aKK76`cI93(64ZN zC`9=f9w@r^zg=w>3s|i^?s;hp6-*2>aoQx<%0@+J)n3XGpZP^w`}qaXeEw{^@o>c0 z+WjRNUc}T8_h^Kzw>U5L_Z}Pi(JJ8SwCmgkaa5-zlpY95bTS29X-+EKv7Rv(!562e zlq~1A&PadweWsDeK0!sTGq*o0mpnQzlk9J1BoPeb(|`y%a@ES-0_pPW(C#I3xrE@X9}e0N#0HIJ6gnigXY zQ6l>Ystfxs%35zoM;8(=EiHzc;bihi%}R}X7d^;s35mcrDg^URDGBx#wifoM*3uba z@=Q<8OEkaYPl$^;un^&?Y)_qHQ}Giw8BymB`hVGsSQlO zC{l~V)zB%*8T1K=u0k-O{h@stqW_o|OhdD)O@=F!GBFAUytnO92 z{gSBpxHiH6>O}cZA%57$@=%EXUrPZ7pz3>htDM6?)OhmHwg{t_WjR6wO;+5VaH*cI zSo|OdJEHyl75Z|KYx_(Q;wbwImvkDgk2|j0Bj)$BMd+3*19(7u=bu)ZCs6(WR1PrB zxY6rA!M!1-(0Yl-4H&e0H$D)5J0IMS6y2Y!$|>3n2*OW2Q}MzpxNURya<@`5Q)juj zvA%o`r(CCI-KulJ$w+C34BMwn$CfraPT>0c2Qc6>Icv=-ui?Nfd^}8?l&1wB)y5WR z{6aaN~ z&Yp|P(ku>LyWBNlXxPN1p1qOY%;O=Q;1~+RFa+kQlMeW6fS9m_e9M1ygR{EeyJ?uwou6zu$np|$t_O`pN=C)_!C|yUhelEVS z_;;=W94xd?y^tjo{HV1dRSpQsbZGCkVHrqh6M ziSKLo@Dt{cl{T{@m~Ql3W+C!YCa*!Y+Nj9r24{$~|VKi2q7oEQYT1#9=Vt*^{VDV1CYHg8Pdr0jY``6A#w^|?Hh zMwR;T-MhNg%im|Zn&xPQcl>)Qn=VnSQT36g7C;9caCCzHmM1VlJb)3jn$FC~zkr2h zMQW*z(pkFF{0wsslNB3L=~}m!ij&oIW>kJ(><0(n_3$-yTl(3K+WWV6pEJVlc-k9kGcgUn_N=nKF z7_LKDVV%&2Mt>GI!~z)o{q`GLxzIRtn_pe0t5ES-!G3q19Sl=`MNCsCj#FHAt@HzU zIUK4IGy9RWu>h7n>*WSxqq) z;7Q(B$Y?j(n;Re(CEj;^CSd@nZ8pwR!phQ0*V5X8jh*9!PD8HUC77Ywqol<#rgT-O z3*=ucixAlpX6J8ZjSn&qc*X?1j6Pqb};@nd0zBuf`j+X* z8aslr7z;m%9ruYnwut<%Hn-k_PR=GLxy}RMO5gJ98?Ks*RVmqKJ+^Gn7iVqd3vK+o z7Oqk)*SIHUVhHN0+Unb#m{>D^5y`MWAmXDKbfMx}dptU`g;hgRWbyviTWf6ku}wr@ zQ9Vnj-^QsV=TCJbLF9K5*zk;r=ROK2s=gH)d-9^mFoj)jzsu^5G9Ad=HiX&P!cG!F zUOeCRE3hABi$u2;p;K3~jld`DnRvb~$7VhtAvOpG; z6?BlydP6#k>%jyUInqKS>BoAbUv7$+wVEI(0$4wFK_BpAz-qOtcKG9%g*R`wEPkF< zis~&{Tw`bUoi?84;Lfdk)9C%c_Eb{KH=<<9Fu{7Fr~!=iWaignp;w8xHv@21r8iNP zyeh8rO2TaP3=H&OY;0`oY{xXtA!R?_;!naSDr21Hev_?1_{No1jQBWr46K5{yiGca_K$P42ZTyZVBuR~qxy77^|jKOa87K&qn>SeHWDiP`tvbT~L#o?R!ZwO=*!Hs_ z4+Fc>l@}?OS_+KzMZT@2XXvGsa5D5*UFME#wK?C03%JJnCl7~^9kKyNe=io-1&V$l zV76|GOHG12rS0bF^w{@i%%4MY4H{oay($_yf~-+WwB2T@c>j&ho;m?(N_K5SeyOi| zz+{DUph>}Xg%rTU$#H86($YY@8_XQ2CXiwR4^KEO$Ke<69M#djN8@&z?)^YgAh^A@j(V%79xDcczM zG7*k|nHFRf+t}jf6krsu9-n~wzCqzYS7X!`!UFpg17HZPuR|Wd=n$;`#mK)Lw;HAw ztdWs<%&%|bkCXW~L>CP{6fm@#cZ$4{+hQ_jRRCTNKSN{|JZ_)}AU#7c6EsIV;3GTd zIB%V@CVsUVQB+5+)fyPgPKVvtTAG8@H#wOd$iUKXMdWb4GU<(#o;Sal^|6)$4lcS1T=dE&`dzM_(8%rgh1$6iM5h;i!GSuhYJQy^a=+YbE!|m%t%s8P9TwZObnHA_rvbdBJKLa>fN zj?Nk5B0NH*=I%`KZAP`K*!YLf?=#1kU1ULi+4=BcM9Vqx&FpeRM3L2Jk~diFV2r;$ zKaPRjX6?Rt{<9~x|BdC}55w`Y{k<@EteNqn*uLpFO&xWv1KWS_ zMpe^>=QoWx8L=jqW3AEg-N{R}By;p;v?NdW4E=o3v?rPUb4?{w30A##*P8;=HNkZN zDG}a>$x|FSSS_s$jrHwMO8OK^HBZT}BF&ku40B^f7}ba-zjd4jwxDZo7yr(TcfirWDd^NljP!t!B?BO zgGh~g6)rG-x75p`TC>c{AtmD^a(n0Wvgg6A0;CpOI@nkkTj^U{S)RD9qcLeX-ZLwT zTpdm9d$7u?eDec6f1wsTn&j5i-Z#DZ0fOZJ|K4z_8+x$&{TDd|#(MENegPG=Q z?vkX=I#fMH=`i7W`Q7)TPzHcK%=$!c%?8ENzg?yGe?)8>tp5JsuI_UM*(xt-)^};i zuIC$2&xz^SnE@}R!708Zadd|@ks`AE+vR@!p*vi=Y#JSj=5*UJVu)Hl<=J3pw zW4d|hOR(iaKb6Rr6_oohL*_71$led?sh^Z(B0?bCNAP6)B$(LAK}b9POWE#XFVhf7 zmq8qAI$;M#pQpH^wLUm@ZJpRomdeQMgbC4CWdXd>P_lQEFUoZU1AeS%Z zF?ldJ1PbB+jQ$=d^ZqxcB)79d7+3`;;zB$9g6TaTdtfpXAkf=Y6d_{JfS1F8niA|t z^dntz&+VtWhAdGqmXM?HZ1JD7Ls;9zId;pB!u0u~bPGY6k|%!;L` z&$rTUidg<+LKsS-K?wOG7eerEZSBnh9fN#M@!qp#+X~FPK~WB(0-6$0If*M{8%nz$ zsn5gq2EPXnL3TG(IBDQdtLJ85^ML(*LdE~rP>92!cz~cniIS^ z?UB$V3SJHeNVfO?W9==VvRtD5VY)-4B&3w?4w3E->F(}M>24KJKu~EEL{dNrr4f)u zRFD(_NkIYOn+K_L|7Wes^?YZYb9ja|JbS;tch8E+E;<)eQ|sCw*J(2BJohVY1l#bUxHV+gb5E?>K=T# zo0$G?;Xo;cR|EZ1x(-*NQv-~c^uXr=)~$k<$j(TwAMre6j1mi$3q=7ezyreI$FcPM zXEWA>Y*-ODT)F}A#qwmaYRkUA$Km6p{W=BgyF-qz6?v&nz!_gbqz(-=yOcOVJ2A}T z7S8|qm9kGH`DPkjurBNM3D5}g{1FK_9)M5K0r~=uWe7yUE|?r0xz~KqFRF4?EHCE4 zd`;!FWjghU8~+R;vMM20%5Tm+1&MO=&sUT=>oBUKJ8Su7D&V{(myO+$V=d)eW0?7G zLXLLya||4V1tbaL0gV1$l0tqoNd*6Bs#C~ZyPs0w-az8?isLDN^7a}FrV&ZBGtCKD z&IyB!vNF@(sVMFpw#5p(v`pntjdH14-RG?%)Z6m&2RtZ-iijRYoo))df#tZuyJ*?1 z;%)n;+k9|VI@4(p0ev*0x_%FnYEt7*CyoSJ^K``)!xlP;2YIqauEMDb&6ExOTG{o9 z#PUfDF*}{eyCk*qwKsz)N{Rg)OMCM$MGr1pmCQ~=UO&&KO-EwY>BLDll)Dw{M(}^f<-#icoIg+YH zS|`E@*zq&J>6gl*7zK+cdnBYi!_+;U=xgPl=-jZKtDOoH;0`P1BA6Lx5+Lh(heb5e zp;PwfX@t1aWwYr($f%W4;61whC7VkW<+q-C$Mv?h3&NM1#&X4jFX&th#@fQi7Top# zNhmN-fz#uH=5gp^)4YgiXvuZcYotNfzO#`$;%|Z-&G%x!#Cx*+IbQ#{OLBs65<%^X zwS-BDM0&dP5P}NlBO!Sn%xki?(IwQ-F$?*aICzAX@-cld`g`fb%T%5;{4v6mYK(m= z>f^+8H#b>WF}q6uSKpwm-sNz}U^voqRq=z8AEP;&HuFeFD=||Xh?7&)@bgn9bXLlL zNEx#L@6~w+k)^$dH}DTZWqxiaW$gcla{riMYZ*qmb-TA(x1GMQZ$$JZPPM`#4bL%n zmp2|#4OMOv1jJDLAry-n&Ix~&d+BHQj{5%GmvNkDMAvn#1QWhMee1CP$-^mRM{N8j z$od6+KQeN`eSN%9^0;AHO1qcOX1K4n#rEed9=%1*t*JFSQbeWuMniWlm_37v z_XFBQvHr=!F=YRLRA-I$a?6VgaXf2M3d!#F3?vA1hR;g#E?L15lF^Yz#w=YrNE*iu0~;stk$u(a2m zi@8xK^q{Oj4&Op(*3dzSm4#44PJ&@mVbVd}97C!mGMRpL8Z~-B4+W*r9`$yQpWG&E zV}$f*W!vSUBlGoy`Y}w$41m$!FGh1VZ3<;X;!H-&Hq&@1SdH8#3{jSR!u*)ubvnLj zlUjh6|5=P4{Pso5a-#{~-#vkkUcTHbAurt)yFlfW_?Y=a4+dmIpVL_GP?>|tSv>Tj zg$|)cfnm2CkYmxLhlr)SI3HwOf+Z3zNV{hT%YRKQaUqFYvFeJZ<}%rhuR~P((Hz*u z2lQ0#eCZ6$YVg!{vXuO2fDPnh;@}a|Sbz3Y4lw$Akdi91@ndr6AE044d$FLtB%XU} zJ$3yPy%1(9JR0?E8p1q&D8C zWh%%HtE8p8>LY{M%X7OXO+Brr!0_?jqe3`!btq2}=?`u2bP5s2e+#UCxxY4D#khVm zYv7v8^P#-C0Ct73#Dxy2RlU`W5i?c06NnxprW-kK34)xqM31yzQ#%Zs?mzJ;8R%w) zY_1fs+r;fAH7&j^$mvfb6 zNRc~uw1OL3Hd!t57wbR4F$Yccj)dnZv+ZI!`?gWYh;O##p}m97vv$AUBA4H;Hi8U2 zFExxKiS#Iy7h(tLyB+OmtotgG)${Qha`XeOcaKXO|^xCMC>K{G$|Q*i!! zR-Jb9!5zn1^=Hg6slzlG0wiMvj=WbE2Sz$8Ezl4h;6(wcqs3LXkWDqky#JXc&tSKU z7h~GY60@7TQ*%6hSSFdTF{>ZUJEy3fIXT_T!GFig>*9r>VQwWZ6liKRwMp7sJqHOT z_ZkB%WjME=u`jQ7?gupVzyJAKy~EW_SzVYw!32sTd}p zgcTIvo7o)SP6i#&E*N)=kbfj>dDFcc!98T`?k7J)`=Fp=(bW0F#ElI2IUnzDBiJc7 zGdxF=DoCRG`~}E8H#U16IPC(%>zD(s--2_B$A-W;qYsTv_KW{mz0OxpjDF)P>AL20 zs?CQdW0qxiP3G3r+P<0FdW*)G$Yq~E)xev=>@)REj6jO1B~P1=9ViufO5465RaRnTnY6;G-&d`H@r+OL6GtklXRInJ-2V z@GSY=$d?ULtjEagd_{ZbhU4dZVBWdCjh&6}&$0t5^r9!a+xy0BDR(eE@ZUMSjHA0+ z@X%&XQO7^ZQuA4{{?(1P!J3a9en=@#%D9b5(Z4ms5LYBPrTU7@8dj|$i!S4bjYB6o zct9BZ__O4WXoJz;gHu*iL}ZVUEpN(i&8e}WC`xObKtMtNsK`}%FUn1S@-+L2?Dfb_ zSqBP*QcII1i5-ol6lzeGT#8aNGN#wpSrHQuvpaVwxck2}SN*?x`I1;{^BQ&}3x;$u z7iK9I8ig5ss@0#Ne!<&G^o#v$Fi&}X#yq|xF-5Xg3yHL!ma(W^TJKXENs#3?9y!%^ zGaSg8#QQf+j^P7l3FLD8Pw?sApm|?iyhTOKna@-4o_C5faj?ZFx_R#-K?@x-J1h=( z`H^hBG#XSRnj^Bs)z6q{;pni)M2soQAs=TbGw#|+8~^?5AMk-RA7Fl)nK^+*uH2{Y zQLqP|zf*l`LLg4?iGpIy{Bt9np>K6K{96Wr`e7u56ju)EO4kLnlej{H6<>-;C{UHk z4nJYNB=2BNGImq&xj*h(n3&Z1k&gF7S&D(ewX3xLT-nB|uAi8#eP;}ErV2`?Tj*?@ zGmmm6{>jN1a1b-cz@6sa{cjxoOLUt7c?nKJtkz5V!Tq%oc!A>k!4|N-3^|SrDA95v z(?3pw6d~7(gI=1zi70z}TO1+_w2YIl&ON#&<;}ywpe~5_3d$xvXPNteXp@uI9}NaJ zMfMAx@j{gKPs|40u?ox#kmO@$`WQ zn=xf4hqJF5)_Mbjl+GoV>dTOEh4hE^F;KwW0+Bxk>hE$neFr$-`(4UeuNN?Q^{!)| zm21igc4u@$!+q33A6P@MWm!tUbAF02V<^;JBh7T*)x$#!|RR!^$|-!S5COm9X$ z{s_!F=XbYtvNt*Ex&c@OX@M6EXa_&d`=VIxnb;dd5lJ4;mi4rVVl#S{iBKDN)stL3 z#zaYSZb$L63AfVp>UBl=7NoVb*G(Bcmud)trgphX5Imva1mY2C@BnGBW9ne^_Zn>2 z*VsU@P78(WJ^B;>&1CN$yRf@<(s3OElnu!wVOggD6^`(=A>6`Quk^OeG_eT1Iq=$g z+8`!{PN&u~W*Wx2 zRAkC-o3)17%_vEfy(ph|b0r+{H^p%KNviwIj){fw$!ohJNB{dAz5H;y1C@h_f`Abi z1c*NPc?=T70vJIcU6_x5S#G#x6{emu?6hn)(xx3Q{MS-#8GWyQvzJX!p}*@jeTf0Q z{z#VmD=_r6yWffFAQ%w+Pzs^M+{nP!C8%u-jtV17q6!93!RZ~{`x_!<<_hYV&mx?L zy-$6|s*Kup3EY$-1@p4orQKo!dpkA!|6Lx7a3u$m~q{Iokjl2e0XI!7{QdEo#ktEn53PLz0FzZ zn5H#DaR2Ky#iCDl@v%E@=K0BDS4h93e=642a0L~fA5-4a#>xuB!QE}n-CXQ8E0DM> zyCH<1F~u(sP@NS?Q=BR1Vz((&GV-X5dk|M06IEO@BXB%N590-|;6)I<;Lb7u3j#97zfoG6~;zil^V7 z%}U#xW6Qhqu-Nh~djnQ6rIvCHX)!rtRt#|4%z>($~nyjbE$DD@xj*L<6t%6=x*s^Ii0hngBa4BkS? z+`$I;q--p}jVhq6)9I)1HE`h2Yv6UE>X-x9dwTi=9bW!*-wOW3`l$w0brxMV@=n>dKkzs;~5clvp`~HH{igH+CIp|gH zSv!zQ$KABVHBeOj42OED*F0{^Yo1TW&BGk@kT7>T{V?*{F0JohSA5}H&Cq$Uc<_wL z_%ps$gh{is?;I_fT%(q(o3)dR2dI-cOR)&OeXC75_mI1lm6QV3__yOmh>T0k_B1OJ zT+2$f7>q)g1jrLOn|{dd&=#Iah^R*}Zm}Q9nbT8E=pW>h_Hr7h(dq??7>kUVlM^_~ z+MMy!^)*zIGCh$geF|5)21mJ+WuC!jIm^^jA^3 z7JV5|;F;$%LX>e9d}j$1!&J<}J$&ysL(^*)=;-;k9jA63VLw_ajQ zWX#2$ztF(x&iPfrF_gtU{HJJeX?^!@nh_>M!pukQ{^%&Pi!6MkDkxc|C~ z&T4CCfV9IPl6^(i#@x=!+R5fz%KLEB4QsMwkD-rTxKCqy`X}t3_XoPd3Vdwt!QTLa+ADqohhc`#L-M#WL=&}Qrm@KHuy?cul9z{rks~p_U zwlSe+GC}0YMl!x@l2}f~bZ^$~Y_ztfr`a;!UmmM_G}|a`2QczeDXu%Rr~uqrINjkq=vUu-_MLfU{NrMznH!6unKPYw~XOb&ZGB7WGiz z;@qj%+xy7l4eU0^se3`ErH|G|qyCMn`k}ucJwx?rF4IL4w$i07`|t&yceT9uNjUPR*hJSnF#3v(CDkHtY6* z{T-r9(L+`HxJvO}Je$;AT|!7MPig%9*C+!nhftL0?1b^71SZ{cbG+l{ z$^-wjR748p9a~UFxZ|9psB7}C58>-IC5x_fGxJi_@msn&?t5f-N8Xb&`o~Zmvj9ea zZ(b9X<64thy-^KW94%+a?=YVZ&4PV%iGZ-pK(-brP7Thj( zC>>o;c0&Careg;F6HE+U&C=$Lh=j4MF3tgi>ybI@1n*5KE0Ng#Iv+rn*~F!QgSXk^iLc zi>#nW8Y|K`^3}^TjMwXV-#>f+*nmMfp$?w^|AL97FWM60jh^94Hw3$oy!g3>r+1^? zU9taN){;}pSdo4ckUE0NEhbK?8C$sx>CNB|^D1Ax!BVfhp2g^X`D&)#m=}H^`54~y zDET-Hq~CgYyRflwpKc63F3D!N{(TDyh64IVnR*4}<@Jv&--^HPwlv=(O&oKU1*a!# zcMnjqfBGjhnmHrSv$o48BPXTWqBI9i7?qCMY>eD|V z2z!0)yParcuT@LP?%i_zh*`VG7Fk14>+ZjWRSJFQ!sHxH%q@J)O)NamY{YzgI1Wp; zPc5m#qwf{9U}A(^)sr7#inYNT*XNI<^bIS64nm+4rh}8Om6NB5&DkFY>bPPfK1jum zc|yopf{Ze!=KlJt*K&=W4%o>KMF z>zT6hF%L}u2Aa7I)CCO`3Y;04{u13A*0<3ZC9b{ZYWeW1y>EF03HjA?pN#Y@!T=gx z$1n+Sv*_7TGKN_M*I?p#X1%&E;VsSB(nzVio2?nvzIn{kNU&*ik5#NdA1Oy0HzO+x zQ1Ak3gib$^6ONvcVLs883WxXZ@~;5NTBbM2L!F45B-`{FUb9;7L!_;2>`g6P&yFIS z64G}t#4gXv1hgT8G)ODe8htdDr&K2VdgFJdSIwRHnEcbunOIlTMMOob;#<1Kk-QyIhfaDt`W z!HL#XVu(v~r85!Xk&K(Qg#{=fJVO9}m~C&p%yf-#R@e-Ag_x!7HnAl-^YE|>!zEP= zi`OC%QYNNm=g*TAvv@-i$c!HvVv7BK^vE;VJF{&D#jBUnmr87?U)AW7a)yFrXR?V~ zzEQ*XJvLV5)m;t`Put}OGjKS%t?p^vood0OFm zR7blAtHvSIos|Azm0l^OC(?oKHSea0U2aRg{-K_(hc2mY`78%VYYnm(kbI74gC|IV z9g_#6zt{7EgA{aXK1`G)SVSRTrhco@YbGgc?O2-(p=r>ikl3w)mmevx5boG_iM+B! zmP=1e7e3bKGMduGOul07{Fo+Xz@vf!r7rS>iWY$Wi1NUm@?dr~fht)~zlo3tbc$M$ z;`dkE^_cp0*ff~$r(X`!uggih zv|KIxLCgDMfqe2cVhRg%9EYYoi{e((pk+L` z?bFHrxYICXa$GP~F~)GBb6$>cw$`bvcZvO^dhZ4$ZD9SA2k;w6C;<-;8({SJLa8d+9BW~u z9;q1pxTMyyYMfXR{T}vjW-=>h@(7yn^yw4GDDIEf*vpyJ!!M8Mz;l9$|wMdqjfA%gY5n+)fjWIg*Tg4faIg8_9byM zbkhQbT6GD+D|q-|-Z^{C&d12w&Bo-or4)2Ly5JEq?0#e}`;2!}-mvcVqWO)@R8#2? zx&e8TTomUD4nJgG&B8{9Vm0-C%ewE^l`~0mc=Dt<;8IdUShVyfP~W3Jg+enZC)C07 zAA9YH0WkV|$&zf=H{`J^mg32TK}Sf*c(Zu$hOhiytR`J2Uwn6A4-`{4f(hZaZAeBy z3La`fB)UG|#@+09>bs@R#quwTy`m7=Zv)5sx$4ZLR%Jl7K!OEMa3ItM!ZW8|W`8ve z(Olnbz-DHH9CC_~P8sryG!c7T>nj%0X@vG0d)m=0sU;JfFMO5`o=d)v#y}tt(pBky zWixhvz|%lETi%3hKE)9IEu=tF#3CWa)MDz?+nx$`;o=5;{8QTPH;lph_kHw!4S?m zUwA&_sf7`wg2I->G>Zj#^iE|bP(Cub_ER3U+ z(~BzgkhSUGTnI5gY?xg8x>jVJI*gU+KkxbQPTONn>3Uu(3u7*3wlP);*;Rd0W|3hx zUqGZAv8cIVJrub1=q>}_V<`Cr=@0Ec5dBHH{g2Ar;>E4Ua-uX!*Dak(MY7)M{EXnL z2#?q**WDZ+$O(^42BeN8Xocwe(wn!zm#yx@_!`NfbcQrhy55+J_sGS_VtnXXRt4sr z(@dZ{CrDawJlCY`f+5i`S{BdRC3#9grY3pi2Nd7rI2;pf?<=JXAyLB1btojgnw6Do z7DDCeHJJ_J<5yk};}okdGflc-8kHdI`fXDYa&DoVP(Oz0$Zhi<>&6l1ym8D1*%)A$ z4mAS>I_wGKTeoN#v8kjwh!74q2c1q}^0&Wdo?K^dPltEq-6t)iQV|wG%J;v15@@MD zTqI{nhYD`&LPe{fM%DJ9VDhXa)%j;?Padj(l!(sYb5)T654v3i^82*w%0)7YQApYo zmLW2hPOc`Fpo8tHtz$yaSE~@pzeDdX7%8b;9JJzd{Y6~c=C^p$GAh4=x0gd?%s|Ei z$k;eDsN}MpfBD$>PtMQUTk1ya2B5Lf@7p8ck>=z)wl05=pBWZN~+ zGwFj1PSzzP0f9hd{@TojEfISaf0RMub}7tk0m2Ab&~n+^2o!*xS(K82DA(!_(E@v3 zefRMR)~Bm!?s-NdzveVY)#$XF;)Es(n($iKnOd8i8HkaZ!bwctET-`}rOUV_4=Ebx zO_p}UD@YslmRH04LXG9XWh_?q7NFAp%z@2y^*U|_hJ1LMF^ydDObfG~cI!|fpJGE; z!IVeScK>N5fbuiXNb}K8lT~in`Hr7h4tTB@sJ(=PtHEFLXemH5*8pGNxpG508+Ugf zuj5{gV2y*S$O|U8g~xrON!$_cwj{yP%`1**tI2gBC;U;xE28`>VgJ)jQ_n(W@m$I) z{>rlbs;w?4oNXEN65ellt~}kllD=|Ck^;HYP*2Gp%kMD!m50iE_F*om-3}7#*MD;^0{t{qxqCFmKGkMOS{b(SNWPg)jv1M zeShwbeb?-#=A)mJ(7(Z8)Pi><{>|Hp+v!mR~n}^{_LQ3YkU<(98$PX-(EG3MjX` zu7&{DiNjf-s)taKzMXdBDjnOUs4TXa(ZuKI!k!klWghd>b+Qx;T@91AF|#o9Fg+t6 z@s{KJ1bTYzC;hdKYqQcFe6V4R2=+Is{TG=H$f9{7{9AoI|I?d(n&%XR}&M zHfOrHM?}cPhUK5*x{OhNpDUsVzPQ6eVRe2ZJ&*+6pT7R^Us`$J4wJtd9 zqDkUIBOt=(HfeCxw~hHpd$o%P4nhb$yE{K>F(5pPS8*W~`%eFPqs{R*oTjz3+{+i) zXs>>Th*TDrH2u7+13N>g@B0N-x6S|8BWz< zZg2;hw>BlrVbb@`PVc7&2Vcw4`bl@`U<^`nhv*>B^3m!IxP*xy9o}Z>{Gq_Yggj2w zQfD`qsx(ku$SpCV!VCvw0-Vb#bhHH51%O*t+`yh<;(DAs57qF1L&T{~s?t48TGD5W z@g;F{+m)u@cKXwmH(VPbZEC+c5?MVy8G*F817C_qmW_pap{oJQRf$!CsgJK+Ey2xI zdMw?zl+`{qCXG>Qb3kQpnk_^yg5G2N(vT46~_ZC&; zJu|dHPpd1^^!L#WM!pIrs}y~xCxeB-N(#`9K`bI`7{1FTb3hv&JR|QbFGn!el~wxk zV?$os#2?av!VN4Ds^0ncCB#x~hVS<4pg&MaDWZ7I3`G)APIx$j3}WMd zLiWG$@-L9xYo15JE%eJkFhvx;Go?)d)7jx&h#Br@#V7*%i)kSC1hUTz3QTSW3_QIG zBB}KIx8mVK8OdVtE4=R_3&s3yK9mQLVV~ZcU=Ok$KtLR90N^-ud>s3eJbnt>!q>Oh znx*hN;6l;%KH?upzHK{b_eI_IM$_-?*x61UY9|<31)Amy5vPYEXPQTix_F6=^6cyy z5T(;K#&7M(q54H$dFW1) z$S=&#zd*i3#UXL-e!*LGX5vEvi<5Xn7GN!Fk65=j$^_lR)Ck{b&f$opN z%a8Qf4Gx7eBLY(yeKA2j92J4Gy2Qo4P|q(Y-_i;AsL5_Z1;OW%FFl>CeUH=^U=eb< zUM!3reD%Y4aQ{GEbWc{Hld$>J=IYz)HGwLJ+@jgx%6p8sSz(dy+AJt?&0bMkcuVvb zjP#jzm{r!j{f+*TuDR=1Ck9{w@rX2dI0gu!4n}_u5Cc(G;e84oYt+Q}x0hKlZ(q?R z$qMAT^!NwDdTx;bwFr3m5kQ9WZACP+JG&JG?7`P<2yPlw^i-%Mt!B+k{tDPg<(vUL zoNIjsZq@+Cxhv>Jc-D03qV;u=!h$B3CbMI-tt+G*=SyG`&HTP=Ym$d9n#&{*NLiKg%=NJG(0M?)0OaHOR?vkaI z(Wi=4PEjecRphViUW4<>$*JnzbjW3*H|8ktJ5InNtoA&_B?|Y>)bG|kJiHq8O4dl- z7B71`XI`fj^#Ki}5zeJ#IJ=(t(=OV2@HSC#KBijDwfn8!Pd7t<-O%CWt6VS|$UFQl zWzqc|F5ve=`!8_MRX+M%wR%Kk)*bvYNgi=-S8VKC^dZ__)`THKo;0E+++hL& zZ;QAFd}6&&wEd)ni2&($Fz?*C-Uc+y{k+-UPh_B-J+ZP!Idxwrdhbc;b24A6BvvWnIB#1SO)JnQYdmGj$KVbW zL`ONHehSkO17P&`3+`UnyRiY9+ZF@|;uTuG0rnzXTx>gb?rRvEG`{XHeszMEA1ycv zLk~tTK9wuJ3rmxR9WzVoa|bpRKZchLR3F^IfMquUJYb$GYZo&%2N3<>bmcZ<=X7*u z=RUXDUC<8_Z-y+bSZO5~&g&s$D|JQLbUUv0)^?HUQ(5-X48FFB7l%{aMYR1SzhU2s z8&c4dP1%;z<{vS3@frDJNC;rF3%cCETR5wJVC&V;$rZo9# zi_+B}WmnH>Jud#>vU@G>^82_G;E;!P3f|ECc&<(pp}!#dalnRA>}fPXE~0~o>+_HY zRKNvy&QeQMhu~q>_ar`H&*)R-s)7CLv@}Vg7@KQ^276 zQU6=iq$T8y5nqKZ{Xa)=H>t%WRV9_dlGjf@{ij47d*0n_+%33{)fNZ=uwSsSGy32+e84cA z=bP_z`LR~US{CAT86OElQ#|FOcwhLgKlnW0Cdk8N-BJ08dnE`*&gj1Qp2uqAypT_- zLq3_B4HF7r8uK44oB;-L0Y-nXyFOs{Eo3s0rICcYj?u4jvu%-0k)^PsJBK~G$9YA{ zx#9${yA>Qg1cI)Lo>vkZ$zpL~nne=s&k{gigq!NtqS#OUi@ z_c|wm--ic1V_WdZA#zaL1UsL9cs(CD6PdOL-x}2Rvqdu0Uf#6wlfofSV6@kiqk2#; z<@e-IOxW>UOFaU|w}IqT9M=0+ko#@m^!I~5OHpPN%ibaB{*a)gtuzp#1bss%1!BBs zsU}uqNV*S__M5}*p=_;9W_`6^R#x90Z7MGh&Hp*0H~RYo;KP?lmkY@&>WX~Vq=M?% zOv+TiMXOwOd92H!%|aeIU~@R{U3ByIVdvlgIUSIG119Um!@}XHZ!fQ{bBwX?g%(Ef zx(oCyl-fW3`T_nfiHglACZW_F)TmbMCCLrlHz@r#>Xh*tn^WLvZ3EYYqj*$h3K{vJ zdPme#@?Zcgu@er!=RsQ_8)R-V{TyIDq3iHNaK*O&dL)~aqpi?*ACny4lcd4H!%kn4*uB; zz!Bu4#Z}Q?M*L<(oZc)j!bBCV>*%6dG3V|oi4VK6{=&OE$;hZ7Cb##InolpZ4tGX3 zCv%kjk>&iQkUG}vwjhHgSjrX1Cd2#(3*Z+JU?7|TaREku4_H+|U2^mS)7-lpTamsd z0-77|IcR<{UiC#1oxW=3ErD|a*j@gLXQ@&0+H2RzdG@(n9&FKlSXa?7lPdfwl)w8! z?>(4zu6*6j!rR;1=E&GMItpF1VQ=UGn(R#dE7N*cwUq70l3z4=tW5B$t@e1zZOCwwMQ7g8q_+Ol%C!h!8Ylv_y~y#;t}aH zfFSB%^!HL^Bi1;>^2zGT!QqRRf@e*Kwk|4PDKj#0NC$Vw9bR2N4N75NX(Ay1t%0KW zB8pFhmvU~kUL0j2XX$I5-%CMRX)!{;!#U&ioR8M+=$}7J52{_ipdA(;e1mk?lRR0> zBSv089GN}S!r%i(8MW`P7&!YTlMtzSl!6ae+A`-q_-m-|lf&jp-6v@x$N63zShI%I zu;lNL4N$@Pf7mz%4kV^Roct%?iu`X^lz}vy%RIHZ(6Xy;`Kv_80*gBp#1UWD4 zs0q96dN3fgskMzYjh-j5-HU0&G+?i{FoM2t-$lWt+9AVc@fOqjSvTQVFSRzSJ7hvX z-j8A!RQI06gDFX?nL~vn2<3$ODNMi@@*mwbw<>9|F z*_uo62Wem=0#Zk6{StwZyyLHp*Y&xNZ}>`C5AAxEny^tDYULq3C8boyHwW|1X(=~r z4-ofrIF>4KMd}5QM4{;|IkqqcdalVc-|W|Jrz2C31o&iy#wi?>E?jL1(fbk;5ddfF z?}`=da?f3{p>aWKfm%ThDOgm;8rG3AW)C{&BOen#1qqxWIscoMdjHTSTM|pR`WdHu zorgUUM(g!@TwT(}QtZf$_JKj~{S%PhuFjAPr{}sYvkJN)36Kq6fmRKc6 znB5`=^Ul>im|5F^%T6pn?(iR+fX#Z*t_Tqn>n~Ye{al^FELZ;-KhG2X-nCfEWS~!@ z_`A^ncIYSXNF1fI0bZMt{>_I`AuCbzzeu-Y`%r>@r^}&!<~OQ_v^m-t{ZpupSOBBH z*H`LzEavdC%ek|>%sP!) zi*qM@^Vgo^(zNTnT@Uk8}JOl^UKY2KV3}WLyL8iHthMB`JpY;sK%)kFym-@XH z=U^raqrJx{F-wZrf{<7rNf$NBYQmTIy>**mFO+I%5ys!P$uoM?ufM^&jQ}rTPSFA& zgFkiU*gJxnQwv9PCvI+D?qih&4E77=*+td7*_apU7zr(;Gw7SVo*!v+xR=x^lX3+a zX8SQOLcdrsx5r|0*i0AFvJq0>v}qyS5%F?1fw9pFwS#u4A-CHjFZsV z-#KuwX=G^b5$SVsYRXpABzaTT_vkDtMCn`3XA)MT#3)&uz(wl<^P;xe3WYfTTCOmR z5p9HIHgC#DGdYZUEJ~-mEmAP=+#cv<;Q&elEdKAUfx?7te6E+fo1k{-mI)rtU@P`? z@l`SoGDou8!kBF8vkaX9g}FMPHVnCBTo!ID2Vhc``6qTKQU5IbFm}&Q+DQs>$D^E3 zKZObMbNx4u{Ma+A%Ac35Eg$;*zK@|GMxpjbSjJ8R(Rp9JfR-)w8QBR;AMPeicy&s0 zt7noS<$F3tXQPLyr9QlKO&61i>vEn!IFPDy7e+YPm_S#EgA^$XucKUbaHs$ouNPEX z(a)?{zpYiOFbWJKtghqXqQ`zVeCtOXIeSaH^gy?M`4DC7o?M2Ja%t>Kgy&axxL5DX zn496>5nEOywPB5 z0TR&7j>HOh;()HWAXK6L?p?PB+v(d!law7X-pu&1-jCtMkX(3t>z(YPhBu*)X&Mq% zSwPVE)d41MWx<&g0|iE|Hw~Hv%;WHL+9CyFntIJ;JQgtUSiZmGvVWbRDOEV>(UWHg-7KZfd< zh5rPVf~ICcsLYB}h_?>quw1lUDzb4(PRQu1l&maG#3ohR6R67MUgT|OXrwLYZru?j zH^BN*AXTZnyksHE9bjxGpbG^q&!vZ$I=P!WoxlWS{(?4WNHt%lMyJQ4D=8ZJ{_i@Y zE5AyD6Dmw#5%0&S%{MFQ9Zd3J&uf2@uzqv0c6J$4t=>#b@tS(_fO-Xnt5N2966BUg z`a>H$9UBV}c`*8WY0_%j4f7|t#r4GB-g&%`oE)ZDRPV+uui=U%pQ7c}52wEPBI8ax zragStu{a$KQZjhnDv}`-m2E_|u{L4(sCS!?)Sb_Qb8@x^?z%s{%Ye}HT|5h}hxil4 z7pJBymNAK+_ZQP?2feAejY|vWYqft&-jCDf`}v5??AsMwYu$8{$<`;I$KzLmbZ~vr z7`v=kWh@CrAWI$9?u7g?P{$nnC!kE8@$y(dip>~uP3f&Rwb^fKxvBc(A(tnIANf67 zVYfx_@}p(f9G>-4MvMlJ`OPK;?q-j#;#zaeMn)QgD4)C`qW=u!c{sP{xjLDGY%oW= z<3$JgJuVi44!iC+q;l_8*Oos;?`WD~rnZlN&IT6D)Gi#Ji_7i#nM4w*Z1h0X1$F7~ zo|;c@4trK?z&eWv^hoHyPtOK?dJIVn$_aJwd@N7@+3}Lc+af=8Fg``rtkteC%Ga)3 z%Y6Sv^z(|z+^Ez%A?%40nBx5;+K6w%7CX#6ZD5_7jIFG;=j45lH0ojTs?S{n%l@Bx zo~s9_vvag!wsJLbJKFQWHwy0mxu63kT2D4!Mp4Z?)_rbSvQd|TEa9UB-8NCx>&@m3 zX%6!70=)QknQ0cz#uQ2dS7!(6wI{ND7;(H6;T0^#C_A$BP#hZbA1oXLcFYAB{rv(A zt3;;xj^I*6)JQXrwOoVR!h{k!Eb~GZwGkt--~7Yu31HU0_5~UeZ+VXj+vpoR%o5u9 zdHv!lrwKw=4k>vpq`qZPUd!&w&rg3mf(^`OMAN$GXYpM7mT^a3igV*2GqWV zTMZCubF+M6^4LqR2d%$dN&gMjlRlE~6eI}=GS5c{3u667Ylr4h4?RilpoR`>cC;4m zW^IpiLZ=(-|G@tE{{@`Z|mj~3%9&n}B0j0|h&yUokyA9qm>Dh2VWWxf)TB|kTFsO_l&^Uf)D&{YN` ze?m(Uc!_|ee$i%S3~5Su5>*ZUnYsB{bm7Ho4%w^kUl_WC4UJ7-nzS^NOU?XUzV@R5 zrdqX?f6N1ALr}}b1^)~A)C8Q6%J_{yC_at!hxRc<$K=82@8v4l^A)jUN*>w$Y7T4K z2Ug6?&EfpEdU%NG%trf|UNN9!&mV}!x48FyE7*Hcpy`USK1|Si7ka5lncb~XsH!e! z1@8uQt~#gKp>A&Wp!Wk7TLXU_qEn@)@)4VEY)x zTFpDt2bh8q6E_C6LEog_bO8at1McYpbO$#Fx!YSXLqRCWsA4{8V)jS+FKB@#xzPwZ z3#UFx6X0T+q_i(Kh-8vmJqgxKOSSS(uwdaWSCAWc zgP!n-zcAV_gDfk&f8*p2e8)@izuD|aydf-oY>2NAt4Dh^Lq|TEn7J^%=jnM}C&}t( z%G(Kf(2wA=GO5h{KJC2z3I&rja8h+vg?^_Cy%d+GX35y*H~xGkn0L+`^#zrt)*d!4 zHXhJ&1hyck#=U6wQ=>Z>1Ne(xjU5Eu*qOU?^~L;~ zm#oLwl<~EJ~XAxyrAHF1Z-I!Sbb#QOL{8z zGO8YMl(g85(0nj?*cjzVz97~o_dN1#?LoP>Cm)t72?AgO@rX2d080`2jv(q_^!EU9 z*yMxk(sA6m!vn7+ zBY7L)+KZ9ciRaZl3%hU9ajXTtusOw!15VC4`9RYgBM{AYGIDnX3Esz`flRH7I{ESy zGdazYf8t3o4Pg?<7RJIc3aQ(mtFjpHJZ{#vSMZt$Xt|w&!xklqO6~u)+2mc9YdiKb zrW?uTy6yTN3^4kDlkcBAfZrSg2eAQ0e=l9)uQvNt?!J@2#YvB*(WsMIW->9nDpjMl znfC&dSG@WFy!;3(2|~wj;FC(=G?~ zz3Xa1BXSSN;DWW0r{DeJWX%UfjgLTLUj8K)a3|e4`S8uhdbHqr`nP@dzS@fd1H}cM z3+cH|VBR^9Or6}kEbNb>TR=|$5BCKHDin1|?NelCF9{a21RRFOY!rzqyTc`HiOJ7O zlGlSU1u7lV>1Jx&-4J9~QJ&TsSimW=eYCCasa;kPT^e{z^%gR%kdKLjM+l^2`e5|; zApPPUHe!?UxOec4heSl!Y;IiCn`bN4vcAV# zH@Yz$>i1ajh_5Khbl>_Az_fvNv-tCzix@2-IR>hUi2WFyvY`Vix1zj4r`7NRsnfe# zu#lEQIiU`ok70rs0HeQ$sf6W4YzoX`^d)oxR|bh2zqmCdqqidaZxJ^b)jp#kc>`X4 z1k-hO<$$cL9BOp!ny#I-?5E))Oqh)~FL5{A3&b$3LG%Yapr2Y+&d&B0CQx+C)efWv z+u0x4>wmt~3+A@RRXOncuqUr0;G1cz92TR#_7M%!;#(#CTKL#6#ZPpbT|6=mf7?iQ z@@E_8zBYC*u7(pwPm+2T+hD{}wp$|^wDn#3pIn^52BaHu82vqLR(0$TCf#K>wA#e3$9mdlG zYkuTjY5K-@%O@%*01dMMzsk{BZnDT~vCw5fHc5M2o2Wxh%Q3o6#* zq51a@sn6hekCZ8dBHeB{*b2PGVm$F)jgsKNn?mcd`djS??Wp!%Ql#OzS;W3z-6V|-9+w4xQhN_KGpgoIr*nX zUk*a7INlk>>MxNKatEX%Rbd*Fkh>M!w|>UKnVdpQfYIOU!Ju|-(Yq@dB{wx!?A4hN ziZEZH&NaQnea@I(6WX2F(F9(8B&VpBCDaXAJWLZ7+k8=XLXfBDuQrxvIMo=&b;WsF z{!Re%&Ur9E1_|i9WomzNumris7hKihBjzIFukq`2T>49|Uwc#*pNEsSK6*K0 ztM&h5?Jc0H44VIOy1Nl6NdYMZr6i=gOAs#Ijg-SXnVo56R(xCI60eUrG=IR%Jj(W#bh~-*kLmhN z4lz07R~s5ze$dl6<|%nFI5kK9F`2hFj^)vPmT$KCBh_-xD=#);s}{Eip+4M20}E<6 z9c%Oir~ywo4PtAl=Z`-7PvLYjEi9L_MC}G)E(B_f`ifi0%z$}t-Y|Rn8{FKmya=HC zfGu!dZ;CT`nYGm7=D%n4lMPFe3!5XR9*N)<%C=4R$D@}kl0F>#pE zQY+M=u7rW&hw!HWP-oQK#q#W8^?6fmf;vo@#w?Uj>acGAnrPp8uNOc)D;#agm$5cT zWgX8UQ2*z##%#0$+GonqXtMwuR<2R(>U-s%6UxI&Bd2o%z0d(0=*Pst$K#{YKj+!x z$|-*FNUAZHny|mgY+Nkz$9j=&L0dM)w~68X)C+sh6Oi`#yH$(!O|z-JhyLhfP;!-E zWhdgHR!|nN8$*Ogsen3%Uv|)Za{CjEz?t`)CR*U9rq|+IFMWkCebSJ7iM`sPjHcGw zvXYvVlPDZ@zd3|#m`%0NlSCKif)Xlf$cGXXqM4_74k}ewdkTm}=1mEpF%GmN+Gh|! z$%E0~FS}Z12Fzr!M(T0G@Mh~FQADwE<-wiXZ%pRjH*53?wnI}`VaqOHNuarz{s28O zc-ELfyy*_ssJ4fhLPtJk%%gAPF%4iIBH`%?!2bAB1gJX*3OU;xxmCc82>83_vVz=0 zROYFkImA0NGKj<%FN|fR>D|pG$`L}a;1WW2(0!Qeto=2{B*2ifiB*Pp=~mWxT zSV)FUhDOV-Yu69iTgt7t`<*dwSs5nBd6qKUlqMVCahY3}?2EE5Soc9OIk}y2#K;+J zP+pEJ$$N6mmbk=C?6{To?sZN_9=-Sg(via|*xingUiz3HjBf?iuCT;obgm?WbC&^QAYQ@ZZHo{t($w=Tf3g**nl9%Ipd9i3vZo7 z{5`KCusrtU!X`M1D;#B9&PZtyqE(lBJr>n-? zo)~f!bZbu9E zqA4pHuvIT3AUY#31|sUxbzl#Tj^FG5fFjSy$@uPd@3)Q-K~2P@cY*h=5DyIuUd{PW zz@RG;Y(D=F84-rpnyIR;%rUD!OE9@J>oLx&5HR;{X=ZjX3NMm@YqN6QIT54WgjE)8 z-MxQ1lP;*PFMY;!=ZCIx)L4b8H5yE3ox%x~sblj0Bvb$Ee+^y^(^*4wUX13b@KNVk7^x%&s5KHD9Vt0OlR%I(uF1=ojIXx>|H?b2@^+)qvBt%)8u)JNQVY!)$8nW z`}AsoyH{q4OOkmOj=t#47pQ~hCuN!xcx3w*pJnSyE#Ilzi;}u7%ukzg`Lm8u(`UXd z6n2T+(RWab3hRtMbabRMf$95i7CJGqcb9t9r@7H;cGd50ldBsyd)K2QNW1;Ai9~Bc zA3_tLU{F!MiVjNuM*g;3!F(8D5JEumrJ$GR(we$N)5mCk_ZnD z5$Ckd1HNwe*H@`2EZb45es=z$&8v?P_*1!mf3!lBaMlM;s@PJM+6vddCx)aWGNQtD zKH(*LUp;TA=|QnyyLSEI1E^OA6-N3DASiV(`g{Fkr>0Kt*v^Mzi+MqC*6>y-vzAHz zir?a^I6lrfX6|jM0K)*vXncB)in7|>8f&F)I;eK6wfj49@(pH@4_zMz?H!|201syw zfR>=Kjtk_-jsgM?HfiAaI^AggXHTRDE2LV&S$s;QX^~GFW^}F8*o3K-kp2AGIv@Qh zTCFg7@k2!hI}n-rEpfr)EQy~H&oab}dtb%o6QF424d61S!qVD~SvZF3n2Tem&dthy z(RC|GiC4x4_Su#qH5R_;t+Y^M8u@4n_zGnqy00rwO9MB}5n!4)w`41eY~C>n2=Uz9 z5iG6PGK)x4oOpsQ(jmXj02 z&D81&1T@@nxB`xWum+1DbrS5@Aa;HF4e!^OYHb_!|L*NIMWOup#IF2zhjn{K(ZD?E zgor85Jt^IIQMyIvh3lGMqg6zOZl>1sp!jmk>)6~dBgmKiB1yWJ6_>WQz`TX%N0N*y zF4CUFyFKSK^M!-^Q&>{+6OHR3(4|N4e|Z5vgKDs2cEAWa>)bs37pc=wlP1niqE|K6 zE_8do7tPV$s7n+p3yO1djK*2bM~!UpQ>tYTmfv~r zv2@cQ&Xx^&4+Bn*nR`0&TtC_rf&Fmq+9G$V_WbK=O73#Aec_BqrLl_+CKD|Xb-0HL88qN zFahga2B1Se3X zu3+D~<~d6iFOHHqxZlkYv-~0IqwXMXZkO1D>qO31z`QfBm8q+_=aD2q)fVvWol5)v zRQDm1NyXS7P>V~;=Psl6XMbiEto=}U!?pFse3*P#jR`4w-|RB|l*SW9Va<2VzU3?r zc*QH1GBbJCkY7}xdsHApzguKL{tO~02VnFci|&81@n04l8k%ZV7451MB#I+n47YWv z)G3U1{j+)G`XEJq(hssRKmsOH0+ZIEq>&e#P5RW>GL5u1ALbceBUIAl8=;vxWEPsI z1oPk)o$Fm22wcgGb0*pZEPLLTXjxCLt+=6!{l!i1&bnUTp{l|~>aeBRy!%x^wfff4C5-4$X{Go{T@%gFC^+Lni<=N7q-siDKICX=)Sd(H(#9x=+Ptm~W+T z4}nbY5%EWXdGL!4;^OEES|-_^_!yw&JkFPM8VdTU7jM|c0j&iv|V|4l9 zo`!4_9dhEc?xvz$9j7e4No^A_4=x~U2Fj+I-#xlN4mO+f-y8^@88hSZ5`~nPi_Lkx zQTtf3SrLd@G*Bi?kc_=n+etgTVyBKN+mTPDM}qTl>>(oYym(k|pbu`v)V;jjiI7Qo zmPXO1pT^5}o^-tzXr{#kJmbJ~a10mpo-bhHpWuRAeb1KkH{W(IxU}`z;)lWbny3F)(j1ipin}4s`#u8j^!)w2-Plf)%jJ0-T#v?0xfuJ zW`6frogbAHKZoU~8ziJ1g}tO|uxx&~zEbu8J^$IH1InMrD$Z@jm5OqSJ)$mB$4}i1dnug?M&Fs0q)+x9;WB&?5>i z1R2{Z(I6w*1D4>_xP`TywG$K~@FiXaouSXCpaR14tRYxFZaB`XE|}{Kh++x0tI_vL zf37w43%z?BMVNz^?EITq#-(n}3jrm7?89BYgCDmOEN`vqF&FYjG%CZ+fH2a>03A~Y zqrYEtA!b!S^De2gR_bO_SKa9Et<0&pV2|5g_dCXDdtC(o7I-;q(QRqd2F(_cUR&ym z{j}yk|GCrEoj2(DlxX>dClt@4B)n7*g;Ip^uMzXjf5>Q#+6x>sYuIPz`wa z0unH(+hopA-d1afr~SdLkvKay*xusVUltNp)LE-mtV^c{bQip;w>I^%GKaXh@$&N= z-)My_Jn2PG#=gXBTK zJ(YA}X2^!l(kJS4Coe>oezoqPGQHxuPg*fqFHHQmRjFWZ<|Eo?5CLD-iCmq7hyNl` zgFl_zesZ&`ut<(=rAo-vIxR~CDY1u^Q{u=6r+(2cA$cO} zm$eNg)uIyjqDG>gMsRO^P{D|14}uK(yB-n(CgAo&6E_z}Xno0(_7>+8rT{5c=6e4^ zM};2Im1-pX%Fy!Y^u7DT-IF)8l@}Q5OZlT#B`#k_aNznV9U0~3HyM@|Le5-~GHCtB zRci>ZN*3y)M1_$)0_cSLKLOOWz%^ZzDM5s>hSp{(LrPR_yvD|sa&4dNizv#0-Kr0G zIc&MHc<%buPAKYrTguSa`epj7SO|0QMM1wDPPT8`XN^zLLn+*Ha{@OG%}#3Ff-8sT zv>F0s;!7bf%70GYz3B!?tb_;+yHbE6;hUFyTQD0C)vH~`-`->=|FhhDflDpPN7KmR zq3CO8?|w`!yEGG%pNIWW!wT()HuwZp5?i-}#f5S{O zY`a1!n1UrTHv%U4z(+tk%GF!b&jq9X)PpPDm5}A`9Tjq!n^X-g@Vnb z#w{zIUDGL-jRJ`iv~NgAO|-uXdFuRfb6NyzXcaJlfObS1d^+Cgpya{m?;$D$oz7YB zE}}3$`oXV?+0huZAEIgzO5<|V>+xPoUg%Bmau`H3akoh+=vcMCa(rzWe%bkjn)1>0 zNLDIk7bf~|dAGNq*@%=5hrsED0aOvDBD%dV8{v9ykeTf{^ehIQe-o7QuI<|$yF zk_Uq$pkN$;(cc4Q+MmYlLvzJcZ&09q?^UzC#we+_z;mZwrq1#zrDcrr>Uw#izzQf*pqRn8PQCs|4&yH#i8DnPHt03 zeT4n(I|Mt(W|YsrLSV)KJO{^cK|{*_T462p&(#_V_Ru49k=%zwvSi%BqDx9cO5 zQoPa^+Mmk6~TDb8w?2{N&>fx3i1l*Q! zgu14n8dTuiXPUs+RkI8O`F`1?(93ttQyMH16@?$Qp#7}w-rf=&yYOLD>4ET8 z*B`r|FT8(mE9iwO^EBGP+Ws`3DqVV9*yz0<#W(3*V#dem{^+xEBeV6TAf8LBxDgS9Sw_BQYJvCM`VRk*2 zy?yA0P})#=*e3D zhYiQTF<8eufYIN}RTs{qQjaPFvSAEWQo*(b1TOY@Q_V#ND&e5r!UAl`aqx23((@s^ z7|DlMXZ)p&!*r1krQ&sxvD%LI8wK0xWREc%E&wXLrZRK4bhiPydLUa4W*!`c-Ojo9 zh3B}2zaSL9AlFcHht7<5q|oJN|09z)u}IGxfdqCbdy>b2VVKpao1Axu1BZSJ3P}tJ z*p}8Vtc9jlm{!{7ncxDRao{;Pg$wG-|F<2ms+5kb8C9-#e8?whX^)TC8av1<6ErJ# z{Z_caET4O7M^SZMhKS5)W$`Cc<-B-@GX#;^w>Hzdz^nOFv&`ToX$D{ku8cXTNC4Cn zv{(tyROd|l^z)q0m7-_DnsTvzFT&X}Xt)|f3eol+g5b_>;sy?6M z*|LbswqYty1#LGo6S)hZzCoBQJ*3C9zI-2n<7QachfryK>#(%RB}4_D>?9b75KNyFiS(%wN;B=_Np$R@`( zYUAOnrX^-`>eXW*i6LasjU0>q810kT)*n8uCxoxqp|zqwR~^O)b@2Jvih?o#Mt=`e zi&)XcD_d_aX4!nYBQ_y&pQ#HcWMA+buKCnHPf_(x67X^uOuKl2w_}-k_zkwb9+4KY zI-`{D(TUI5y99B+HZUy3g8JCutSBohTM%q_bv&s-2khZOT{Ld8-b?K z0}JLUc`!Hz3d#W({XJ0i>JL&rzabCF(yKszZ(iQH&^TN+nd)Jhz;IB++xzR(M}ScH zhJ&JeE?X+ahP{N%4?Q{8Pk!TaY7)!gZ%VL%w>ZfyN@`F5m=kvqQ$6;w@94D^veIYacGV7d@ zU>u(YA3=6Q`{Fkm&`t*ECU_2x;W}pGpWxbcusc-t=u5ScoW#78wZXGdsbch0Xf{L; z^(ESKy%A^#5hhnTUv|^VoJ9Br8{Y>I+)=o2=*cwOaOXM1lz+m9sQEqAdk)vj#0KgW zK0bqidOXoS`?d}mRKnnZP?UE zT{F|vgOn-PtF#io_oJ;x=fTCIf`~iVmf2TaRG%{&K+jL;$Hc)$=yE%z4@Q3v(zhXZ zJxwnEdWllMeAi9$ikf@_Zqz*WI_f*fX4~>XYT)HCkhG{;tZ0+8(-jTXfm$IeU3KbGWY>2vos0 z1a>pGJt-srOqFx0Z2vKW#;Hxh>fx27+4j86;mFWOH6vr=&mQp_&%Q0zq`0_@dP^Du zuk<@Bwz)(Tu44FW$WtmdF6A5J`WF#9<-4Jhj{>C({ymZ)sG;_6(O?E1dEtr!iEuts z&6yS2xYv}k6i6xbQbpq&F)s%B{+#L~mR12@M0}D|S<}kA4nzJ(iCs?*A8vt{xdS}} z0%M$jBlx3<8|ay8Zs+9$f+{Ds_du(xbIMV;GPst@Fu(`-qT9Q?6|a=?GzQ6kdDIUp zZTtEA3AOjOYr*woXivlRe23VPc5TUsgj?#2raR)>dha~Db>|wa#RLvK2WN0WnSini z{+#1T`Cq~XeC6CyQ;U-#s9}=28qNMFub(?S8!SP^4MFP)c}J0YT7~unuh%Uhx-fmt z$M{8bQ}eem@4h}P6qNq;u!neYJwa3&@Nky;Y7g2C+M9z45uh9BXMItr1mv2|Z0>xt zitpI|g)nERgAVsUSU3ahpY8Arq7{CkVArvPV5*6_>e|aqJAE&Z!-WGs4^olWPkWzw zGp~OPfBb2bO%9KYzmA_-UEq}q@o#i$WYO?vAtg>kL(c$9aK{w~bC4Y2=m9G#aCBTb zrx_lMEM+H?#g`NO+dBKB3AP7LkBNj+{chGF!tw{-tUuvbaJ?F~5HH`nv{S(z_(yyo z;JGxWO=j*)&(Pb&Rl4dasN`UrQ2!5@{>=&(?siFT@JqQ>{yrp4^8SK$?LCjIE$Z2p zCp}&IL%**=_cE9we{0c^)9>KTMMPYH%I+v@<1F*hexpM&n6w3>@I?+}2F!yyu0XF8 zncKO;ZiOB>$j+;(LO7U0hOz?&qbN1sX4LVE@Wb{_tNNg93DRoWjARRHFp1BSu*H@%SjdpPe^dnqR#`BS3@`H^=gs;wR-TZ`X8 z{a`l}zb5VfWzayKn|KVM-2!O9!vd)wXju!QY+OLX$Pkz}Y_?yc`CUk6+(mr050Zw^iTp zd<2DEpOp{}CTi;)FiOzcW<+y>XdMz;khqFiNT~Etna+1F~qo_OwUG)&V5U)ijT3lEj^wsvyz;^n?3aQaBcM_F3zY?tV4 zC)6A%0_g`$QnHEHp1)omwC5zZlb{U_mSjNrN(aFi&?(_+qJQrtj(D>KjVo0-cCQ36TfdnI+g6M0gV$$1& zRe}3*R-j)H?Dy#-Tq6_Kh;2qgHGEoF{!b8?Uw1}Te?+O12t*{UEUu1436?bl{$vOE zuHW|GuTbF+$eNV@h)}h^G(mIo^GiOegank;!P{D!H{ONFdz-sBnmRh#@$#OM`6Kq5 z(4Q`SUt<2&bSOS6V;4)CAaALLM565Q=rwNBzx9>VO{P}~2y-<3$fI{VURP@KSE?9XxgKslFMd1Qf`!fE z;UmN*!)^uW85r+Bc>q5-GCg5zfYIMW7VvWMJL zj&J#cGdsAtcTUSQZ2e`*??lGQ*fO0>#P%(-q$AgPLJ*Qg+4KF z<5xG>TeT>EW2&E6wMSjW|24yIc;MUlZL(*s@(ldm-4@2I-v6fWfC>3*AqZd1-oB89#FVSa}0 zi-!tL3ekWExL_v)_zeUgHrK#kCyCSNZv_`F7dJxEdAnd{X?R3lF%y55!-4cLR6XF% z=93w3b{ihcnt9h)rnH9J&cZwH#;vbee}_~C1;q0mH2;(ulI;5hU4uCA92~<147z{r z1Sc*!MawP8IKdN@Ot*4UJQUIp}k8|#$b46=U= zI==a#ifKQ|+IH(CVjYa42lL<##(Y)~Ue{xPCRAmd(?=fI&WTAt$bgOYXhs-g?)uZ` zhG8Vcnj|yT>T#=9cRrUiRO5vCl%bPj{2)cO_&F_vs`=%2pz0NUf(Q3Ik|XaZ!`v)K zw2vVI67|n@tf&ptrD6{JRd8t$_~~)ms2VQP>us0@hRt14mlI@vD{}(T>!m-NF^C_V zh-7xFa#Txot|yRm&GZME#MMc*5r)14M?d)G2kxYqT7lYt)<^xUj~P8*Qcu`=0iIUL zj!>D-q((Z1;x~`#6biTZWP+6zdRlzgp4uGfnZd6G#>*@8Hw0{VjcV%E+%Rs*nGkyJ zA}zZWJoSS5$g$xV`2P{CB@e#wW|{rlcLvx-ud-0x9tsF!%Qsaq>yaf1o65(pJ^@Qq z@bmjalMZ9W>m*x^xH~w^c7CLIkL%Jd3`lgRxp#pRJp8%O)Z5MxWM$ZTSeqSnvj%%0 zaOj`Yo$x?{t=(@y#D9;mOteF5s^&4l>e$|;O_Y(gx8otCPE{+n27A;W$G%+4Zu=!n zt~5qpI%Q6A^Xu)0Vu3X(wT{E6fN9+SVBr|BV=lnx@AVar+VG8TAKHoA$V7UH-C7rJ z5inCGS1ZQY@u%Y@C>x)~@P^y)^rbfpmM)7J>#&LndA_W`qHHj9F(vi<8P)pDQ}h2y zmzj&Xhb^c*d+b(#dhyN~%L_|(xoQ5Ee~g@c0P^KVc(QH=KmPNG(w$|aH{|=?2Y)`$ zA;uRSP`6$h98>QUrDGE0WRLjc?r*dHiR1 zQ&GeForJy*U%S`+Cs-C{5DPO`2=uQb`wF;b&zo}|#tb2#O0+g6$GPymh?rpb9*K~3 zbr`#%9Aqu~KDILTZC??8yl0K5T=u)uYg--s&EG9z61c}cEK3r_H($Tb4D~5s!!d9S z7WlTHJb=;POV*DYy`7Sb_zNZnjHvIFgff@5QmjckB2h4TbMLp&kwGJNFtCW_J+YcxC677qpP(8#LeZT z{|NL-!TFSY$fxU9g9WJY68ap32Upt(6BU7Rhlh^MwBq|NTL+<@{?`H8>x!&xZ)}g> zEU%*+c>-%zh_}w10F3@#t}?4FK3!Ole%*5G`HPow zI;ys{1L64z6^ebY<(>BwtUJKVVRDt4TNa#rod=)f+ifuxt%Iulck!3(NM{OfwvU;# zGe_xyd2l8J1f1<1P70HN=+!wb(Le}kX1UJ3W!_I8+?v+qlpg)sluH-4+P;Wd1%;TT zDC=HAlkIQ8L432CU6rec|WZW z_1`(GJEx7Z3nP09@8b$M;N=?%+BZvaXQ;pyLVhQ!;z^|~04x@`? zROA|yd^X1PvS=68D_;tHO>o{%5G4i;l3X2Og+ig43T8B%H|6d@PbzleqxQF2ucGXG z2DJOzBJ5jfCb-`FGSZnjT)j>b^Y#kGZNt}ba-lW5OMAK3rY~L9H={`p3zy-tdzOTL z32KGn{s#-kfE{xIMt?6;73%8_!Wy?@-_^$*6kH_W11MII5BbKIJHYYlW zQz`r=ggW~B9!1OV2R=|S!8|1o2B*vJA7eBx{lb1pi}~EFqUI7-aDs3~z4y@I8V;j+ z6eOvoDxw{B0u)o@lD?J8=E9I?pW)SCjw7Ke2dF=jJ>;%6-Txpp?eGW8gY%(#xVb{? z-OW#GlAIr>;E+g3+v$7(>a~=lR0HgCEGz4t>I=&yHx)Vi$Gm88*Z0@>4NkjNW2l+ z{oBR+?pZNTx+as!keZmaJaWwC{&(W)p<)rdlhv@z^}H@*ReQWkDd@%G%QE?2$gK1@?{+dbOKt z3PKr`d2%)04RseIZm;lu7kPKp6)jEvtS#dyd;hDiWUv&(&JJ?c&%CQg^_h{AOdm(> zq0OzXS4E7rTkkF<5DX&UX?Rog;3}@1rHi?j6~vAA%!QlX_r{+ev2eoo+b@o{wAU|0 z97GR!-_U-z%KM0}4tXy%RLUG=F5Ea}iJR(k;*Eid!W=HqAv(kC7X5>C!P{q-%91A-v~ z<$!IDlD0st!wamCs|DLTO^TpprKvf@6k=v>?{)fAz&^tFFRM@Hwj@d4wnuO6Z{MjI z)i3r+Ii$%q>U$j=4kqwjZMUXW1>BDh^BHjH(}-rC@^kw2cEoxInDv=~AyGRp=0 z=T%|P6$U;ss~JUH?u;|AG>$QRBS_PmtF_V$yXtjB`%FJT$%E0~Z^kU&I4hB8ah*nA znr>hz&fTiJBP2*#s*%rr>8mp$?={jBhz@WAJ?ckH6ecj_`RPVbzQ{`ba$^yh#$F|+ zVBaX%WdQTw@=8DguA>vIR4mkV0p>cm0CLV?Wa!q$734C9HGEcG7e~fb6&%YEB|U6V z?+w|M-??Wfc{gW^ki$>RxKvL>)534ick|2rvNhU42s=4BS$4<5BGh!jI-?IAov{E$ ze-Bl9^ALr8aSWSplxHt0Qc=Fwt)3Rn&OS7yeN|_lCkfDe5tuDJ>?NACEk_t}=Yh0T zvuF$tou$A>+GkVW@0z6Vpnw0s0p`I$1u+8~4v0wpg=;7@u8WVZ)wCteU80v&kGOz-v@(rTnME#PTp`p`*BsQEqep!?suehxRx$+dI-k{7%TRp?au$5B6gMxEL zA38cQH2?9s4stW&71L4u%mcRN^7~{Q-XvnxGOrng)MTlU7#=o$K8=VdU8M|Cp(c?P zV*I-AB9UZzV35%i@QBex4uN*}*&8HKWe3STNc@*rSg^dSgQEwCwSlwp>GHNc*dK2G zslypUGroSKB2fLyOm(uVYGB<~8DZ)@bEh2zFL1RS_y{oM?xJY)UtLkU?ixHexwL(ifp6Dm0>#|v{>qq_KH;y$FbhUfZVIM5%;BI$D z0$pH>GsSt&E4LD9az94Lc2H=-hGWw$dcNH#5Vw@OLK+;gKu6ls|3dIL<;R=~^zOG8 z$-XIdzHAzOnUVh;BJ${6At8Rn1m*i+Sx{BlofrH%eT2;Z_G~b%+T$1NgK1ZWJ0*Tn zx^G=)j2|j&DOF)bMY4L|&#^jBoMxM72e%fBXl5Rbc zcraihS83i@5GrkBYUl0>Ienl-Ab_0a7rDULYWGO?4jXlK&<|0^0OYI12ADXA3R@Rt zd3m_6f++bJiK$@By#!VbeD*k#HvgADEfq;-u!!~9caevi1Si`f-Y;D04L>Jf4548-ebQq!?x4M42%Am&&j1wNtRxgwdFoG(& zbMx|FvU-F`UkV4tb~Dzg8HEiuCXfroR7!SL1viGYT8B{<$3bt(z|Z?BMA;dX$DT|Zq|%a z_E6&*1xgwGd$b$Ch=bAJ?*`G?C>Mp4wdfhw9Plw*V~A}cigy-ounkaH-R5xX>+1tA zhrxp!^(pa3Wjdzx@{m3RK?`rQrf25n2(@d^oo{F{#&gipK5z#$S6g#Cb2qosTzYT- z0bPvF5q;GSiL7Bcuz%m9`LH-AEw)k!@6B)spLF3e;x>oNjqb#~tkziCa9UZqHRXbE zb!^f8n6Si0IyjvzT&$vH!CK#;2LS}kQ}SQ{1qybe{m0hGoo&_DqSr3sm;CZ{yL@M~ zxm8=31jqu+~Tj}{@N|G8u4NR7pCJi@tF?0@>V_@GKhiJ zAnc9;>%tF&=KpBN|mcaMFI6Xrr^QG`M9F@{opjIv^WYN1K`A3QYs-`5XK-KaoGAoBoD7CY%*^`!TMismzB9}+Kd#_ZO^yD9ez#S|WLG?0#lL0203F>&zm zNQQvZ_#gYEm^Dy7%yXKpeivCTm&~Yty?L|S{Areesf+m3rPY}HQ`5^og>{ZKL~x$T zRzXZ&Awlzr25ZwB^4H6B!I`Y-n+yryi-NmY=V<3;@8s^v!@~oYGk4DA78V~#64dB_ z8{H&?L|cA>ZeUg<{f1msxRQapJobdbz}pylwgKiFW@rfqh7bV*9frh~>lL)y>Zy7L zE1L-9KcTuC8;*ffu%Owi*TCrSB?`MXg5l4lxzMfW&L7+SmX}^y-!7$@aS9gx#Zs_q z^Z3+-q>%qGeG->M$-dE3^l1ixJS6O;+^@v(NEHX{jj{p@X}|=WXT;Xp4Px(jlmQJg z!9axPoCbX`XF-SL`$DJb0v_xbgv%dp{J>cH%!%qRrtq|EO4JPZN>vX{WD$=_R#@u@ z*Uc2M61M&QXp`Q>thQ-77LBNQXfhba3H4)`jv4qTnCjcu$`#ROS4j;=Np)G5(8jmq z7+xr%b!i%ZGA1ZwE(9-!ExZ(|#?9uuzLYo=hI=m>Tare}4W2Dnr7~`M^R&H;aAXHx z6?}4vyPFk2$<^G<$^7(?2u?re-031lIRmWnM7AABNW?FXk8A8S851kar{1|v!B&mY z-K_bnIa$j9;j)~#ZMv$~R4!L(0D~ALW-e$?_U*wh?;tcNSa^{C;NciDpvs|a{1aqK znG?Tl)^=?Y&IAMZQ#p;Ku3V3f^W=mBBCu9xXQf$Y%thoD*5jwZvUKu+Z_Qlzhf z+><3G1Ks6M^3W%nRhfZV&lM!TE*2Ij%gfDu71&N^)}71+=bE}h%yHT>HZ^3QY5&rh z^_sv(R@cWVHRRJiqcRm`bS4i;QyiU3`P5n@>+l%7Y~5Mnj~) z3u2+cw_GjG4jN)f!55aEe&RLbX77Be;mpXc|Jk6$u+i_%eRb4A`|l3YM`hJd`TAe= z;6tTcLG|FX|A&4>2QT)-hbTo-w9sdg13V+&pT<5Yf8n{*+oeh8tGpgskyx&)4b=IAWQDCeNvT(6BHv_pxu)sKwGGL^iQ@@AgF-^oG0K}9s$#^vyM&YS@171O2weR0ICI+`7Lhw854eJEg~uF# z(cf!#g3n4CIEa?n(fZtdsYZt@b_HR4OY47@$0%D!EVSG5PJkM@67jy3WB2W&`#IVw zRdw2f^FMi_eB+uP&%JHd5s4fF^We0*g$u;N*1{GRlLH_DO>s_>D)_c2!}@2-63|XF zi$(p~T1)OgY;WYW_e0$YMZrUXoTw?geF#14!(P~Y?Sbs{g#u*2GBz9o$6y`v@K0bxj&9-#i(Qg_ z(uroU^MrHpDLx*W+WnD>zU{9PlI61IPQaoUxH!P2n&3OimNJp3@R>2h_M&|BMK+B^ z&UMsiJ<)714}RHMTe{wb*u#)II%}QRs8U#r^2Fd?jFb2LlbJ-J`Rn<`eC3!O`WEF0 z>Ozx`jE$ds-7iqLY)5wJvJIel-o_d;-1NWhzW+7AV78)aFBGbtFf5L#gU?WzI%WWj z{$8eVLrXecZ+2Aby%h4ys~^DBwm$q#NQzjp=wP9z>*asi_YB8wH~GhHvGD=X9-T$+ zjQZ=7RO7OA(aqr;>TkLjNoD~PaHknpuq7R*RU9#S&SbElfIytQ0IK0@M$#T4&-$*F z>nPprQSK+Vc)vMuBl0nuvAO13$%3(>GJ|}AsgEWiwd*hVm~BX3v1cJ#86|}&0WJ{G zkBNhiXCVFC^|Hvlt+o<^k1r58T-R-K2g?i>@OJfhuzH^VHp;+m`WgjD!4@9RfN8vN zsoJi;b$qF2olMU5vc*ba%Js0oIHu}fX^FOA9-Qj{q@6gqyE&Nuzn4S~snb>ELrliG zj#sfY_WE!>h4u5CGK21&k><4gr^azKC=5Z`S_LWzJ->VQWn>lI5}%V=70`QowqWk< zI?BhHg5V(N*M36&|3{#b(&vAM%+Q9TGW;0MsY&+F>6y-#%ZgVM5!JL5%I_080ZP>D z1*g#mCI&Z&%1z;$abXmcF^l90S9EbC;$!@VarMAFIB7C5H?cC~H-Ty=kkABG4(Ic* z1pU57N2XvJ6#p{+hrnZz&)=Vphd+GOnmiWE!7BXJp@h=?%MK2j6VKApJrkL$gLvb`eV`B@K<0MYlT0$I{2bWG^ z<8*TE2k>}Kfc*HLC1ccBC8;8AjA}VgV1^>}eU4@NkY_2AI?VV=jkG za`%to)0`vQK0MtU7jNDlCs(`0o>2r${LnT0lrDV=sE3x;8%OU)NR<&Zs6^(J~yDw z_)Y71#W<{Cm$l4Q@NWA7rMFJ{pwhfgf2Ngi3OQO%afg!9lOfHJarXsJ)b7zZQecuG zV4P3~pO2*p$^aPsy)>n;OnxVxcm3(p6L6o-b0cp~A!s%+W4~*;$!q`RH}_-satd#CzUXw?`;;m z?>;adL13FD-Rez*mZn5Iq76PlAvz`xMt={{>-%aQ6PM!-%_WfR%Gt!Uw%c?Ph#d|m zUXng#%f0b73A`Kzk<>sI2Hh#>R~#V z^1v18JFhb(V9D#UtAX^_A=}0bxTJfhF?@s_)+|<*fq*PR-A^l1DC8t(LzLj+UWSO3k;Bp4g&OV#Ky zNxUZ76RWDL`93KQJw{B5!8@omJB`$k{sf}YU)SwR$b^#g*+pLedD@5ck&P#4t`SmJ zXY0n+_2UaP^Aax54g#XCwhkaW2xcIGZ68_>{d_xr=yKk@^ld!xFB{L{`{vw= zx;?X>Y{85=LO)cmw;+{MPt04e|3g_}rBC^WD_5!vikJMbaC_VD?&Zqo@1gn#>x}*} zRL3lU(cdq(hp#u@k}2YT#>ouGP@1Jh8qInCkbIxJG83PJZj9mslrU_$nb#AD8m^TU z{GnGtDLJ&D~05 zGXJ|+5j052R6piik?z&{srS77XqzGSXXw&f6qF$K>9JYt<+NVhrq0x*b&dGK<$K>Gj*%T5&48XqDagsEX{wPb|M*{XkVgZltf!jZARW>Vjha zm-)SSehpC+=bNz?Tu)*7H*4VDI3IuJXH(+4WMtw>9Ph$j^0)(vsr4$||u zY!(f)nukFU!@J~?`c>qe%t87h1ez~*`V1kog92I!i8$JfuxpV-#dIH|oDU%$?Y8BhoHlJ;7I;=sZeYmHPjY_8s6< zc>n)oj}W138D%7UBr|&^GBYl&dF?$iu9*=Tkr5#)$|f?4LbfO)tD=;>SO0Ub`ThOA z&-3%I=hO4J=kpxr{r0-=^FHtMUdLJpE^Y1r(qfJX+{@uYv476V=(+1fq+WBqqqtEK zS~9Y_3O6hyosKHlIQ;t3>nAzLG=*+U>5&S2GbPu&J%|d6>EBS0dOPaiu91(psGqRp zV6HaiT>OH_r@@f+=4o3d1d_xARn^(}I-D;Y@u7FAgRi`2C4V=TSst(+c>Sx!U4kf+ zsLG+vO&UwysSVsSDT|HbSygw6r9!;NwNOzf?UC-mL+S@oe!u`2{k`6-Q_81xG#Rbv zS+whCi(%?jNqK=?cmI)6My~+r<}KcVLzqxJ1#~Z8P$zR_Tgg^nW?o)ykIHoj7B;l1 zz%ABklMMts7zIc?6AuiPvW1!*OW#A1XT)c3ED=pL(e?8yN9K|l`Z9KWb0DO@^@jkJ z&-=TKt)+<>#Q5lIgA(uEbEAu=lJR27Q2zPi(R)BDk}Km1h1$W6 zvt}g3&1Zzz9%Tm4nmr01kr3;=TY0B%axbgcYF%^8OD#*5)18x3K=8QFfB|79{*+T) zT?4T+x!-`chZl)27b-PRi=QZc=^N)2UbwWqCCu&k513ZEghLVD`7Sg;D%xm~@K%M= zpLq9Me(z#kf<-BwMfSiIK$?`XhZ6{P9TU-?`6Regz5dCJTKVEd)+bR8-!Jh}#W*jB zX}j^<)^JIKD?-4Fv=+`#a4R_eEo|@H=~%lGsShR{I@hM7v!F%lUL^BBV`-@#%Bj0q z9OdS?(#~)7)DLT1o9hq>Di%0@);1!jbED?{#=7ezEm~qADksh`DDovaE2Pl|lp^Bh zag+>G*TD8tx67X?ejLMe>r%y#6|wfa`RY_@9q&8DZBFyV+98XRw{FeaM&dM3=#u6c z=9pV4%wMG%Bji~h_Gcd@_2uikQh$$cD5URt=~7q0HhEd_MYK!9bCh@e8(g=w<3$j1 ziGgI`SY#1A9Ej|x8Tl_+IXLBaSh1?#y&d9HeRTuB+)nIrz7mJbd-wQ@ZtN@0VFAX$fco#wU?@6O_Ok|* zJ^!#JAJhXDN5Z2L1<&FAY59~bS;g?jrk?Z|tz~gQwbDB?;=Nf*Yn{hn z-U;<*3^6sf^Wa3j@CURHh|r(Xa15m04lcI7xwWg9JyOgf`(0pG>r=XF)Dr@%WOcQb zLd$Q35qB4OJT5X|y0}?5CpR_qXuXPc=nZPxJ;T*iFrk5reIHUkfa!pNe}ZYpbez96 zhFK(X`pwvE3}wR1;3ZtjcjQ}!(T{B0TiuU*Mm@65JL!3;gFAl=(~MTm%Bhsv-eg+V zHd&etD4a^5t^;4y$-;SK3$VMP)?A=D#{SL&-nG*{@emq&v3WxEH3$C3*&E-oqvdcl zuDo-X#HM9btRRkY$ zf`cQtkWBm&T;?|Qxx#^)H=f>7(LoWdJVSAB&?APVy~a3J-mS*`C8A{pQmX1GM+VN^ zr~CYtZ)}S$%=v8^+BXU}3jP}yZTYYCEB~nP7g78X4-J$v21$@kFzB%seCX>pj#9B_ zP<-W)tQuytRP~b=ak|~I6K~KcrK9)A4OA^{ouDSbqXPP(n47vBA00vFbdH!)b@4ef zugJg?V~&vZ&mIJhLC`Oac@>mCc|veGh?NZ#>I4-yI&HpsPjcqC@_PGFQ8?c&$c(Yy ze((HAfR}(wzMcy%kq!yH=?Xh8@z9nF{1thf{_WXkZwz0qdiYC7Xj6T&77-?Rw^c6K zDV!ta?(TjZzyHy}DRdtH+#@!t3GxqjE=xVQ`r}RY+YE+xtrd!_I3zh|C05IStJ0jBOR(0QjQ=;`c*9=M%vu#R-)bJ_=RTUSOo<-{EygTDdZ`c53#$9ST8b>AD z4leHq@vybDwc|W43fXC*VRUQz!u*EOdVaV7yCwcpERlgv#Cm@;wRPF7@*G?doaVMr zQwtBM?Xjsr=6%cNt|~3VhmD7X71*Q%6^=UW^3vTHCFxH>IMFi^X+lWhx0H;<7|(<( zIH^;i@`&iHyk^;uEexlFUxu`*t$le1-efxI(cyuB{|=5E;8rn*mfYpO2`JR&7i*(?!hx{>DIbu}Vfv-7)3lW5OgUCFnr*3q-#TjGD3daepC z46Q&B0YVHUY4G9T%t25GqrX3Mh;M13Jy6wWO=0P1a3i8(x zD1nk`%Ig-?s1`+#s1=aTW1#xXn%F z+m*zicf}BmCA__pP&Ikb?uqTC7~N6Cse^k=A2B*+0gV10Dw~-f+adH4$)y-0YZGcw zBY7hOAA7KF7h5JXkSfLNPJzXdQ1zx#F35S(-JL( z#Tj7UiR0PC93i%rhwtoxnwV1p6?j)3)9Ujp1(mcKWtGXj;#$IA!i6pjcTkN+c{#=? z?R8M*mk7RHeWCnmUfz45rdEBjM>y<)WIpj28qDyK8=@)>1^bXX_f<{yi+)`TgyF!r6Tp(hE+22de;yISJGdxU8cG#8%)$qW+oYColL3C&;$>_-_QtxUy79 z{VL62Z&{)&vSGi~s#XRr1c*4pk!o{i{lQ8sbnf}G_@4$8R+?3+0RQ!F80u%er!7BQ zEo~*?+80giKsqJ3@*09#2pISwWM~6ESlS)uAHA6t>{y_G*n0)~jHg-3?cRzE`QyOY zpXl*FG+oiVgj3+MZdcvx>`af#B1{J;@rs}{XG_9KwiIOi{FHa2*2K5x3122w^33$e z6M)kVT%lEV$-WM@JABU2mW;{jH^&TxD+VvIIgWL zP3(QK=fAyiPm-arQ^ku%Ds2Xx))R{e^J#c^_;t+kBLvEmh;CHYsL=$!AYds+Mj`Pa>D?x#d9@I7`h_Mh;xE4BvtP8ZvJx)Amf)&X(w5h1z<^ug%wMK^2v zOaUYAFf=%&TrosHa5+Nh{fd`cA${2SQl@J~E%sn>BuFN5c>FjI)YzzNq||~mCQM&G zhmKXe4P0TiWUE@h+B^^Dop5g(JGq!2wYvtbt4)md?Xrfx z2&-H{?ZTfcrIq-h6Q#syk?i?E?WgTbi?O`1~)%UjGepOW~S#AGS`sxNfVKEfP_f#t|Yy_s2))Y zR;7+@xTB8I6N&rvXWsJPR#Z2S((Xo_WhWqVg}Q?mLiWW7gxXH40^y}iw^1%oULd2| z+fK?b7{IixeBo(_Q^~$$sea+QtlzIF>!L7Tj&CT;Hxh0A5((>Qy?u2|TE)+{mrvI+ zRHz{#+NXU05jdw1SSazmT+*bj%1{(?TE}Cnocin;H1O)rJk!1!V;vMb3G!gLx;8 zRVPa)4|@xj!!#~|Q-=To(PKXqYa}GF8e$cPiGLf*88ZniCDloN>kioqGHo*!bZ(cH zd{Dp<`N9bv&)$-KXB)R(5?WX;cD>)6W4;FiQR#|(NF97WI93r1fYIMeRT07Xr;K-6 z_n@fHl1%Gqo_uEhC6!f%z1drAr&v2~CI}WsZoJDDeQLbdz76#7#)q#IR9yKre8$gh zz2GfA?zi6!Y@~*O2fQQS32;+eIzl}m7KnFR5d|s7n*5YmN)g;-d|frEA9#60sy%QW zSonwv;w2J=7sxnxcSJksY=R0_46)XTv(2cdOciol*oG8a!&RW&t8*J80`+A2VHkjK zqW{Uo0c`s!{om4+?x2w0qM?00_NBY!o1%(ya(4K@W-DwcPbIDm7E03i;t)2Rcr_IW z+K!JV5vx{B!@D5N#l&; z()KP!ol;Fqzz6mr^#hm=7yzTcm#+F+4h_NR*{roc>I7Vk)aiU$GZebjf0!W6U-<%t zvz)==Na@mU@A_t}oDuDwipChm=RH{2AhOFv#m`vbj8~?j`Rpf{cS5a0oJ>Kxsw0^? zt#1ylaFxO(T)=l>a{0-5CET|r18Ul{Wz^*}4mA08c0+9GzgOcv4b^)~c65?535;(L zkTiX}`rLe1pJi@sOY-3k!aBg*rwu+KHsHS72^2v714OIF5;tVE?;4#O-C~+fmoa*$ zXB1vAZr&B~r|k(w)Acp5I1-{H`-{f!{5ATty{s?|w0|v|d&!)meZU6ORJfE>Bjc3` z=AF1H0iAAZ?O_I7PX{moR!OcbY-DVsc3J#Cb(IZBg@;(exDdTl!P`d1s{OQA%Lp1GI1TL5@DMW`qn7sZ zcf#g}t%ld5P1}iGzrN(nX&t(T&-RQxI_wIKyoY#_>1EVA4SQY%=_(poX#wgOdD93$ zi2f%R;Qt|D1Dg<}RR5ddz<)EwMv9l#qJ~SWfLbULvO-tRmY=XSG-J^2>ii4+sG82( z$wS~>8?VN9h| z%ezsfolc*&(`>A@5V_xYCm1*Y>wpI^`g<8$&5%fS8F>&#gK1j!kV-fOzc)b2z>Lka zpJq2Kws_MQERK{hiXTvnk_Lv>NzI|6jLLhK6HisB3)PH>vwnPjb1MzY5%6&C=sdK6 z*_l9KNN*KzlOOCzU}k}!!cpAgX3`9?)-~+6;sv)qY~u_#6tj@GModdr7}MGF@GWe# zX-E0_)#vNqwfEGDzU7M1%Ae!dfyTyQW78cQuw*t-YNV$8Tg0G)jq$A`HNCr>0}=gs z0qfN1@VXD{0mMFa5DbK|b@o3PIf4yg@*QfiQ#15m0x=JB)y4bD(#XMv8{OTkC9TYYuS*FTNjV^KyeM%p-=iV~TqzwleZUSlM4&PX^gb>Q7BbuU|7G zDHju7Zu|3GJ5FN8b?8D6B-avENSaN(h|$AMUhJ)n-j&_-@s_eIZpGJ|?UFTLLl>L- zR*cf@xnl~TqIybS+>=w)USEBVU5W4k5&v&)PQu8|4@L;-J2hAT1xBilm0Mg{9;S=# zyCz!qq!x-)FzPJgRJg?p+7ix6Q1*f4kuqpMdgTcr-7B#m!fA{wb}jGF;ElGFw!Mbz zuY&2TepOJw$+4wxW@ZOF`Dg?e2-h4jS&8vsGw!~#jDHUv>4hq$KeVYWxZK%ddfHF2 zMi@TMOKXP;43fN($A&mbPOe1pott)axj%0dpTYpu!4hUkGb=x@95+x-7cK^4dR>k0 z&$BqG%w_AvC?8k*f%V~#*qI%~#SC+w1MunKKmbyAm=twtPW}sAd|CQtB=xE13@`O( znPC@^Qs@}I{UIWtMsDJYB@%n;p$J?%zA3Yd=!WqtM1-F#1RN#eX9C~?yl ztK=Xs?}RPp28Do!HXJ<>yA^0*LGbT%p2k3ZHFNI|gb%Pn=~phlW9sOAq8sd~x>M-T zNb>8o8v}Zk!t|JMQ1}`j-F0O#*XjuSLPC~w-YH?N>utjVB&l==>A^mvegM`117P&` znk|TUludCp)lYBiljcLscUN+H#aeeJB?Z@`b`*3!^c=ZrI&7wzSExoG&U}+WA@a@o za0gdePQTF#SBTO3XDe=uJmBF(Pd;E{gMOIEO27SzVdUNIAQnjD(qrOX9#`7*`~#ta z+XF_@p~UR3#Y@^YU7@e5&z22o-%nQD+2zQ&H^ALDulObBXHF8GLy1^|=|&l=M9a^Q z$-$kn`M+VxaJ=h7wALL-CSrZ7Dl)rejP6aB2(=pJSrKA&_CHuS0PKK^W5E6|R{o1z zq0~R~@<_bvn|-zfJt(B}eM>=#KsV>*oGmOUtWngjSHdIKn-}JEb9{c_kW~Ljw zbq~C%x&y;JPnDjYVNslvodV{akSh}xTW2$f2{JDcL;`_Z;FNy;KzY5UGZ-I92pUK% zZ-kh)HIHhD@jE3%nW$;RqM}|DEKPzvK-;nj==D(XdsT9A&NAKl#j;Azm4!Z${Ikt! z;)p{N`;a>Lyl-A0833cdm#g37_(ituJkcd`s$>zok6h>Zj7w#4`4~Q4Z`xRZ%)r6o zNSK(;yZsnKkMcbG+Ns*NqZV)cSz@Fw$=4)8h`hYk`om!`?}Q7`1o+xrk?t)-pOe#P zZw6=!|D5_Nd-fX1SpS_Xgp}1ZgFnj~N0re{AZ)m2YE-At**IK@C~sO(Y$n4?&F&iQ zWJNhGruhZ%l(=2hWT8P=uUH4f!AAhnA$>6VdyqCaJf9hx`3O)`(h<}^iFPGl`3Du# zH^*q_%qmi1tM7uvksvvCKHc;0T|FP%6CYUeAkas|t^mKCHBR`v(Veq9y%ru|-U*PL z9YAY0XQU|YUusX8r4Ue$Z&Vljlak+aSt`dHp>R6w^#kY@h#s5DvB z7YWQ zeksa<+yJt8k4rI2g`JU>jTT0?!bnk!^3?e#>4o&04Vv(UTTkPf@n?8hzH2jS=RCOl zFiu-Mz0miAO3+6TQYHwH=#@YZf@{-Rfb=vE(3}orxtc>jU9qD%)ERW~bhL*!Ik|zb z>yay4?9ENai-99!Bigw1Bk8U++tbAqvuB`5EwlXZA4N~21%os+bBOV=PhCc(&BalX zBiUt$QQK|)mAZ;THv>1Ztv}_}+B@HM*cfDCb}pWlCqkNqhIU(FDbbi*5gET))I`oL zkc69Oku#z| z@#eX>cf<^XTXVtEAhPRx9Gu(@!W*gUGun3c;3PyZy>yM)%IaqLzyz;D07-F(i&&!! zXjyQw4HEVPn)mN6yH<71UK7;1uoQs7x+`_3_4Rg(cSIFYGQHKcv22-mo~_ zUZ~kuKD)Jkw46$G8r=QR(a!WV;QeE|gvHVng{rKVd;MUG_ab|1!KF z_Ym`wGt-lnl+9GX^PSJEuy4E0FYT^mC?NyMOVt)MHh1G{jJPRjN z&%!4kF;Sy8H<7OujiDTO@OHRpi}&$<1L&Iwb%Y*&cj_rAD=~v-c9b*2_${Sz6Xh>Q z?_7L`M%8TNcjfK30SS2v7m!!$3^FB-Si>=U&h(mqpM4m*CKwtE$C~_3U0+|Q28RuTTqM8rmp{9z#=p)7OsFyAw$|5M|8gC z;uvCGyST!1e~9_YgN0xH>XjHaBr64iUQ1&0^nr-mGg7&JD7@BqZB56r)x!XFB3R!@ zqQ{fH^jA7bay_Xhk^M>wn0G?CI)QXK3+IFA0pg*FQwC52c88z!d=?$JmyA<2zS+4= zpQ{Ixem^Z#0cnYVsIhxbt~H9_PRO7R`aWgOogL>@J2zFKqOig&K~7g*E^R1_RIUfa zkCiKeJ{bM|2{be3t39j+Q@!()5WRqop0qY)E6iNHpxChYML;5Ul~w$A7e--w-Xm5^ z9dAdh$1JJHA`~R)eSi+B{}Vt>_U*(Us3BW*k#1cr7{p3@OwS`TThh%#=a#!+j>B4q z0Qo#D+k7so9jVT1F&<6SU5)n2(cn(tAdNmZNtHtKhXC*;o#=hy2z9lCxj2J>FPD(e zu^;@jEf>N*u9ZuAz@kgB^LtyFH*fCrb74!|tJm9nhf;HKDFjXSop9a~ z*-F48&K8}C`p&4~LdDzpQMS8?D=Wc&@~{utf$#F)qIPLqM5AchOP&qA&j|UyN%aW| zGxtJ>*|(zOnk8(K0uaKA+<2kCe9ve45suk7xXm!Er#D8*wrJ(B&h%zPKkE=CWki^W zCuGXhl*{f!THz^O;Qr{J%~>#R*mTrPE!(@{Cj7i3y2uz+dBFx{%6<)#g7kZ62hrn9 z&u|Z8F(yMA^g;IzzKZi=c;_J!)FnC&dNq4!d1~ z@V4R&ITS2Ox)^`!J8#~8{v(Lnqm%q7l79E6WI{IL0)e?tdmkdiwgZjE{!K4A6nQ>o z#~bXzS6p??p~EyA_?7F+eCeShjlB6?F9ucM3)+{V(FUsMyCdx=Pn&$Ud$l}edq1fP zl;<(;UJrX$dqXEM4a_@n%W{O6L7*@)3~!>v;4#s>X2XIQE|=N+@G2<_^OlB36DO~q%~h&FF0JVL~Nu@9;5 z!*sCK4qY98XB+!#SQ#yQaewu9^9n#wXEM z*(@yXrE3NvK!!xrfO zKlSkf5=1yY+qSNhBh%;iVy~ymyfwdab`OT6mmA=8=x6*pX$m#MGcnU$PSkiGG9G~W()0}W z!uqD~U0YXysZd?+fnY#tUvZ1)mZ+GP_Q|;ElnXBgVDgFYpwqaCjTA20Uzud~-sz#>3|gWd;QzVao}t<*W$dYcJyr9$=hvK$ z!UvqL^iP)Qv`c4Pze-5lH4N=F!qii_fr7lV9}qtP>3}{M{k?yuS?O>8Kyz%EgmQ?Diu&6qX@IY}c-V%-M ze5|Z)ft&L>G(1<%@S7LPFY&7$$+*?cK9`_&YjWUOEHOh*^Eg2ViiZfZ3HO*jVsy*` z82vp|aw?PoMBeylh109W5eoEPx(<}v=wfFAie~&PYc%EDz~V@GvXs94G=v7@UeSwK zaX$YWWDz2``n(ixa%GH&p3_W=1CRAd?F2wFfk2)1lbrUo_$gzUL6E?W$CKlcI3o%x zdYL{qaV-Lc8`H} z(FngH&JlSqKmc{X0T}%~P;BNNe?~f0M0KD)n>PtMI_SS+S6^Ht94f)x7{pB*Kgvo8 zdYPd~klg70meO<;BWkb8k#YFG&sk>lm@_)`+i>)ofQLT;5<8$*nheAq^h`c}NB|Qv zRdQK$I`i3Wn;Io}I0%(icwVshwSs2(#^D8Hzlw~NoilJh9$lWqZl_(aol zI~x8oj3ou>_dxm2Q^1 z&MfS4U1{+CpZbLg-?PXScjb?c!lPd~#Yv9bQ*)H=Ml9F2B{?fKwM^pSzdU|VGnzft zm9BXwZq!tlR{`Ypg!-|x1uifi(9aH8oC^*TQ1N}roSfjcCuJMov1`mt*v}<*yDpW@ zS18V2>4IYQW-1sJb8YbU36` ztt!V97RP8}zm~VwccggvxIL~|pLI2o5pj;3fXWov{|)%c5Eop;?g#%)segjM7~v5V zP~F$YE&rArlu|xT@J*$>bG&V}@||h;Jvm*$`KDC)0rTuUlf0(j-U~v8Yfqz8N~vd- zE#qF8s28#$Ol{m_`iRjn3t;s33g~=gP3)t$K2lUw{lDsF)^-vUE12g(&c3>QxnKmt zF#|+*_Ms}wCNhU)3VO{5Zpnq3*|*N#Q(IM$#|?jn?X&GixqcqZJJA^jgkbhDLiFl5 zr3L95Drev`do9Be+N$<*dVY=wrlCOM^6<-Kj5^0Hr?<=Zf~E+0+Vawp;BHgGJPg7mUX<&EUcB# zsV(Zs7dlQw-zTV#I^%4+_*YRj^J_W~8Gc#%`~xP*cJFtQf;kO83i)+Ii~vOT*NH+x zkY0k^YzXl=wHL`RH^sc~>$NP61djX;QXMRZ_@x4jy?V^5OTj5YmL^-f%g(4yyxvYX zUS*esd=LC#ZKcv|&}jET*LR^_Hd;37AMsFS%#N>T)nDEL?U;#Nb=JHvr6f3y7U>3 zG&kmSLj`9?@NSc{havZIebw(8dTe+ZZy9>=+&J6%_GO!o18O9jyDsxDF3e)C+xi76 zLaZ&pqkWE`y6jlC9`1%x4~Z12gG;HFRlkMlhnnb1(*_@K!L>9YvUnrIU?~ z9e7gt*g=bVA^gu-vc=)=_#H$I6kX9ZrAp;XRvJDFVU*rEmzoRUl91!cM9;{VG+~xI zhL&_R*a9=IT-!@yDcnC^U&XHFR0_$#36uvfO*q-P*qR=nl;=^aH*4DD4;V3KT)XWRTBUSg=sH6|SM(#)E>5w8h~V+6r3#qvPm5_@BPcApbx|eT z!YV&LCfJ#+2QC8=!puC5OBWP^VP#99Pp|_axOk5_Ufh*Dr0 zpYzELixK=yT+#Q+^f(@shtjcJ`>9Pv+K<}v_W~Ccb@I;z?~9vHjsHZ)yElxYzq8K( z^G^6WprC(&?Ln{<(KYdu-XM4#nqUKU;)>=4-2or%$4RCEkA7R1m^|vj$?tZh4c*S* z&KWkmTz0jevjk!kfB7wRH9X1p26OYCBX1YYcd12$-wbn~Hu!|lFNizWzb4x%_ZvT2 zqzhhr-IA_zUJSlo#BEFW;t9)Heyq^ADf-RFhY&U3`k7AoEnn-zOuU1W*zo2-<>VK; zuLT|3YYc6(QPa1 zbo3!?o-8$JetwfEN0ba}ZN^Mf@Qj~dj28_Jd)BAk)V_5L|Jh7o#9@w(pnL!iP;Lm~ zVD$Iu*Vqp2l`~OW#ls|0fd#0|`se<~zWjtsJCgL4p+j1gU0`vfesM{Ts16W1r){op zTGwSk;J;WJN5(d)>R?JM6u0d899(ly&mN@@IT%~mfgaB$u3UVAoL4~$>O-u6NBxxU z>d0r;0$$>9`iv@$8M!Or*`wmieM#WH^iXm=iHZCiJDj@A`3~!IxK?QEM4OYndo0lv zMXk1&%&)Viu~(i?y?`O^UFZH68^^#QIQb{wa`{^LL_R*QbL1dwSaS@!!_IpiEw^r| z8(&$XhHD6e=n%LBszPf{g+gMFi6V#3Kh3|U;e77NeKU)e-YP|k3-7T7z!4q+=jdc< z4qomAZK9nmz^jWEAbn{6j0XqFY1;!nxJK^Kp5&?;RZd=6tRy(s(r+E8;5#TN0O@Nb zRZ{9}dk(isdhNDhXixo2!&4FdX-x&SUF-34pDsAu+rY*VVFwZaZ$6HpL$LBs(4}zd zy;62vaScwaiisg+rkJ!Fswo)rzVl0k{+&9@`Je>&V z;M{(`=odcrc#de$8!&M~Ynj_R+BzcMUObFUobG}T50INTc1In3SDb1oQEJtG7DEmr zEmiOqL|DOR)Ku+rK)bjc=@@Ts*{dw!DKpLV#VV1i6qMEZiK)QR>-ksCBAjZN`?U8V zIwb#35S<$=pe|UR;3t@tuU^(nZRwlr54Ib+{+sDF+7p>qx<^(KEyaa&s~5E~)dnx0 z{T{g4r^MP8D;Qibvo%3aH|BJS0x)sH^$2o?pq{1>!4ol&Q|k7hWV}&y$Bv%DO40Yx zoxdvTzf{}|#EP=@{pd5-^;$IfrhkUA%O8yxC}X;p5OrJk!gT1$d^MHQ4&ZTWH<_nRxU<34jz* ze?3tsNs(15#xE_9dosW>DtGf+sXX@p`HhE}qxpMrV*&pOloN=NS~`IEIAZsKye6(w zAA&(IOt&cXdHF@+g@4YWsiH8g(zD+wHTAD7exaNgX^QpAzG2utM zEvN%M!KZKYio~xc3@-gf__A@1$R7Z8zyTQjy`twgm_++&H|UigD1K{e%9k$4hZghJ zFDI_z@azQjUP4684H76+vKkafxm;omt#1!^{dQG(%v@_a)+G%iaLrNQsJ{uQ6T%M?b9rJ|%N zb@>%6;*?Pa5= z*hkrUT<|rX+mBdFQqvOp@ z+mhXKW+bS<&SDn0L?6ssx;JNAum&4*UNrhUgPrFo121=@ulg#Cp??tmM+#{aipAmdtGk2Z0T-H!soAB zTZhA@5SD8hf0cP``|C%yjcN^LFz*kbc-PxhmNJrf6I^kkH6vG+aGUAE%kRGr87w1B z(tX+o5FL>JCx{l?em{qOYBF0G=<$5D!=)9BZnhV>Td+~MO=m!C+kub|Bt!vnELXVt zT|Y;LP?y+PGu!6uRA3U=2K#$OoMWM9c?%+xC!^lrPUCEW?1KaaRYeGK0;!IG-BUsp z7Gu@wSItRjNF>oeR7_M{&3rlKljEvC|I54{jju*uaB;q_4oR1N0meJKQY%H|uf=>W zb3OlCqI0~D_*Mw+Aq*~p|K#BaG9(-S1R36ak~CG}yb%3_bSCo35MmsG=FZQ}*d8P>rf9Shy1uXhf6ZVW`E1c?CVmD;18Xzlus$JG;2}S^6K@f7 zpVE$pf1Cd!%h}vXkV^5M*i{X^Z>?@7iTjq*$w%ob4>7mpQX}Ww@rg`V} zbow7{^ZbRl^KEVvA}xgJNRwfo_5nm-*Mp1BzghB1*sNkYD5GaKY&hfC(D z-VsrGAn&^4B+#-2NFk-Fbrg^5GZgw!U0}TZN(5Doskm9m_wl^x;QT@}~hIes)7wnd6y$qB372$mI`>r5? zNz{|Pc?kWEHovIL^>iwhLf1&2aoW4z`y0Rhm?w7NcmOy?!8^ggF<1y5!07L_)TQhJ z1;u0ywE0_+n$zXh?;~WLDa~^t)LgfRU5T9fHo@XZu=MB0d}<{_iA?H-DpWjd)?1ts zVpNCg@(D0Me7eZM76ay;u&-P!%*{bl9LGbbfOT`q3h|)9KN>+r9hrV{&MeI5Mr>yA zC^P1y7gkmlJxIDln)QY*?buQ;ekzUq!Odr2K0*o;_*k@9Wg&(Y7E2N=#TJIxeK<$t z!2oyPcZuKtjQ$>|n!!DTM^lg1FR?6#)V#@{MM^E@FQ5KmA01Bn7mo^s~}5VqbG zIkQssNGL+O$dWHk(N08s=G!2>2I;_EP4kbf6*FN2e>fQ~F*HA8i3%)_Fja~^ce7K^ zicF79mgai75&|Zy1LEN0F-ZTm^%%bXTCMR6QzbIt_G)QR6jg|d&=aFg)0B)QE>TO- zZ#o1Cy6*N1{U)om)Z(a!ocl&XVs`XTl=wNH+Dh!(muBoFz!!B~JOZGpXA^OE@aIa82!CWdF!UT)Z!Os)_k8YnyH*P z`)c>?j8ZIFc)F0WkV|pt6GP z)gEJbP(5dePK^%XlPXU6>GJC0M>kTcuyLF@)gvthH!x9_y7gwOXZ60M-h45$}fpigW*DtlAbVCO#+x|&+k-h;JgpMZwyiv8nm!(J6FWMQ!ABUGi6W8mT^=u#2vkmKt($ zfD}@l&*dyc$3q%$&fsU|Va~h!+zW;Hr{AL|(DCnkn*moF+(<$#S zesZKH7>UvN$|=H}~&VDW#R-Yx1|WwZAB8i@9K; z34cX}=WJjvd&9^c-$LZUY5eEO&Y&S z60PiG%%O}a!D@dQqo{y%j)Iq~?&?#zh>3xScK{xgv;V=u0bmDQfYIM;sE5_BVt3PV zxGvHS%W^y!4mR`IoB4B}A;T5RZb8TWdOTPh2^imGM5pt&J^ZUKDN&*q)_xaX(+C4? z@8;LdYpKmgXo|tS6PbiCs2z9!dcVytGF#`gSOs2^TE?kqep)Aeu1;fJxNc=SZ+VeN zCye+fhUQ@QL*Z);gdcfz%B+VY7c@#orKsu{i85mCFBG))Y>^Ia^uh@MAJ~V~!Doab zKVSfi{vIYBw@Ed*B8ec$#}{g|O76-x2A+G)nep1AYOGA-jl@MJus9N?adw_sj&bAk z3Ni9n3^+{CrQ~9*U-Re}Qi0B4n3sDsn0EpuQ&2(=v4kAD^$rn@t(twxVrVRMZLio;K0&iknldPP*5UT5 z?smrgI0O*bht$F6gYyo-02uu}Or~>yM#y?HKxUgUjWuUDr{_V&wnUt0DA`g|qJ?z!IEt5fH2#hMefq;+B zwuv#_^s6>XWCwrHI`WpcPx}}m1o?l0DDAds%K00*Q)5>l&rGGf#Ap_a>t%J`hTa#~ zO5f(dwE~MHC5dME@&dcj?C52q@UI3*yz28mBD$H++!|B4$1GZ)c+uxSA##G+dO!}0 zOvFv}lovcWHh*D3@;1k3wIX;&-vp`6vq`yaA_o5lu$lYTQg_B0mG5z@_K8!gs`#x7 ztDMm@UX;9CiZX@Sj&r*Ld;UOO5z|3(S4m;L`y_2=ZX`_YiSS z4-Gu~UGDZOjL*&wU6Ybnhf;iTz(YIt{%dmK1>143I1(ak-krix!vX~#O6k0XrG6gF zyYvec(;ps_-4c9ORD^LK@NjH1L6H?n2M7UO0CG}}ysbrQ4U_HOs}4A#Wp+5jUm59D z!!j(SJ)&AK1brn4Q2MSN5~_Mr)#$?Ksm;|Gt~j6Qp0wf;-hewF9+=7`_%4J@o0l^Q z8gcEGU{J1wv$y+F>G{9&SrMjsNoBPvBxN>4gAgSQBm>~X0Za%Uzz6};sTuh%Zr{4yQJLoPf@`ry&yUCW+nkNsI-&Fmy)i`C6< zcVQ;Bj6haPaXO()%I8Uc>vrw}ZN8oCkYrh*u(&lOrbZq`Y)w;j2C zDew6X)j{6&UrN43i>2*rl;lf!w>K&9*X8YE-;5ZV;~l+q-4b67#V7V3HU2xTC$E8^xWlv zacawD80K6ERQ5w`HZ(x^0GtEb{{)T$hT?~KRXvXS@2xFFP7;Av-$qXK=`@p})Dmz< zHKQXo4|0=P8E{b-So|uTG+A53MKiYODtB_HgO*T)Q9>N!XW}P-9G|EJ0}Yu#Y|0QM zxS9bQ;7B!{HlrnM_U%TDZECE}Lqq%(;U}oKtgggfi6MWZK-4qiCvy9Xa$$ik@HVeG z@_mhcn)~Z^+zpwa+ZeXEV!m;j;vO{4h%H6%pFA8uh6wZgYi7&1!2`{p*)%I}G~MQJ zAyt0mFG^N4^=EX5WO`;z>!^;x=r;EuCXU92x!8@Wk; z8`0Y2gc}7ErhsQ~J&X@iHcsEgHmKYyX&omxo$CA$i`Oj@gCxl=$_IHW_UDH)V5E8F zFUEX}+7$5WVSk=n{}e4l^OBM-L|^n?dB2q!SH{n~k+X;k9nKN?W1#-IjqQY3Ubf}b zuYol^Ei}4-%cDk3FFiE9SVh0&UJ> z_Dgi$Evfe@M>sQuQm??!(As4qJv9=7&3T7;_!9YOgR9t3eP z`g`@Qac*Hbqb+;x)8~n(n?&A|fdMkVsaFbKjgfH9_;%e!1sGRgM@gtD_z04`VnS=Wx$^(rAYTdUL7W2lJ_Ha> z9HTX%#&tSwTF&(G%@@+coDjQc^Tq-mLjqpfb5}&|pRsU^aVVnC&YTU$NuYvRBQL)D zwD%#}*H7Gs*OgOLru~PG_L{F;*zZ7na^ax_InjCjhPU+t=Rm(bREDDzA6L+r#~IP55$X!H zwR1Lh0Ub9S`H}CcfJ8tb{UCIAbi3LrH@UNB*tOHqVS;{fH68YH=e^~-)V$tl(jgHO zd~a#(M5W(No~u9ZXLH6B_tTOwwS^2exU3l)SvF+R-f9)fkKFkt#eyw#bxjJdGr{Zi z5R)r;#0(l%se=h#t;7OjKLR+i|IX0?kOxeG5kl5Z&Dno}GqIxKic-`2VivKrTJv|% zn-NB+E#@x=~G5t_tH{}>3{Q>p)H~*QnrnBBP0Ot1Os4rbT)GSTL{!p zKu5Tm>_WJ1k5OL2==DqD8aA0#26JI+98)y$>1Tc%g4KyT!}flFUQKjPQ8=|YOTOH* zx{@kP?-%!yNDGe>W-^#}LW|i#L9-=u3#8=^Qdz)-{**VI!1yit&uCFK*!iCJL8(RJ zXS(04Ox10UqVbknSLkMkI@_$W{TRIcXyf--&x4ISy6el82!B#%i%P7^F*h9cx~WWZqdtdd|kH^G&-zAY0}{faa*1ZCSY--Otn4| zsT5@X`a9h?REQ2Q06had0`PcG*LjR`{8s*)Qa6});tptKiF}*`*f`+dX-k0=`EJaZ zIr@;YJn}No53rYV@3B&SlSG`L&5a%LZ4;iRlC$<_^Tj%o9BwL(r}M(ArE+{e{zA#Q z+ZtuCzMGv`fCV%_82mVpAp~tO`g?H5WD8A}_@YJwp5B8j6P~^3>_+VNu)M5{Y-H@U zzc8x-SR4rs<>Vq+N?V@^ie!u?&0~Mt^=3AvIK2Lu_;+43o5b4h!MqcOs58{g$<7gC zb9kWv_VQ_yGWhie#yCbj$ffAmhR&#dtCKII==p?Mzx8|=r*hI0+S|isgI4!Lr=BO= zc$edbk9NJ7KS{y6&mDbdd{o-S6cG^N9FYfuqs_+sZ%G+=_TH8sL{%rUnxP~UuPX}5 zKgBN`+WVhbuM=@@zWka(%9r4o_8UDGZEW}LD#wT_!S$*^ zFz>`!Xk%#&QqHZA&IE*FKjl_~6BMz;-$!Z1aSj~18_`i??*OY6pA%mQRP8()a^cpn zQ{?@9*q88mS|o$ z=5o&qt8!oc_4(FshP(NT>l5WwkO+p66-^G_bUUy(5~9R&rgP-%y!iZR2DQHpi#O7F zQj`qC7__=e!t2?T(tiRTaE{JGJ1fw3(a8`LCz(QRAhw4u;s~DF8yxPD%bk;vQ$nrc z60H}u?w&=RZ^meQSw=x z^;jZocEbO`!~tl)`-9-)pFkUVrXE7c@G$9_qYur?Gpr;>q1uK7Gbw%uJ*Yu&!RWvPuRTQEqj1aEwrwWNaf^9!MsP{SsJ}%~BsMU7I>Ck{R$Hs1k#F#3DRLc_ZCqOFmo`04lCN~tJXdTq;= z6jdF>)K?yPy|6xS1o8;>K{{9L_hfc-Nsw#ktgeHdx=M@t$gk&Q=OTYppsJ|7-s}PM zPHaARXP}~v{RyWn_JB9IUZsoP84!rkN=cQ^c!XEFmWvi6iSIxzM6<(&mxs&cOzE`3 zb(VU>QRiMwnY{BCPWL7a8#>!qj?%tA&Vxupbf5MCM97QsKNfgc)}z0Pucor(jqxGT z*%p#SYpyAlqmTbFy5w6MBCp$g2vK+Vbvf~g3DefkHnOd`(mej+fuH^ee9en@nTpl!eN%F-Y#YA_N5kN%{bw1L|P(_cGK}Zwn6| zf>GR!C41EIRwnV;u3$*WJH9eh*S=eZs8|c^zz>4zvo_S*6h9Z+ADsGnoOyJ^KJRy zHLiru>>5ODyRX^@8Xiz;qykchT4~ScmI`*vSOQ;?=7-hj>_?Bar>4U>Y+;3;a%~JX zCSC)GN~~Z>c-KRDJBWvs$tkD+$2pcHi${7TjhN92+Vu;|?@)fB+*B!q1h2%PUej?@ zya77c0iZ!M7(c%t0+N9VVlM=q*Ar!GRko48|AC6FV zdx)SAqRCS!D~gn@C7zei>aD5yp6J$4&v(pEB(Csu``lbKY!R*uQU+Bg_Hfi~TPs9s z#WzdH(NI%d-H++SBXbW}=_fVNM9cNI9tGLsX%+xH%n&7*1JoWQUpt)wnXrCly4ogM z7rbq~@v7Uz=nFnp4*Ji~D_2O=WxWErl6RHuAmA!zW(Tu3LsSC0Nk^iGPl>=@CP2v3 z#FfNmT-p^390zvnCu^Q^p(^PGDY#g|>>$pkkSQhEd9Rd*sY-o)dB?(Ne)Dl_sZ~jV z80ONTmeOu*oF-S097sn89pp}mR@)rdM~YdrEZyiA|G6rPk7`rxhKt&HbEN~SP-QF-~8J%6`Ws`NbEG^y9L|8B7iZ}jo1v258`&d<#F_Unbr z4g436&_kaK)uvw1zx}hk>t&+)3-}>&>Ovx9V&!51g&x+qfc*uU)Sgidg+SG5?mRYV zAbDZjcT*$I-_ND=VyAv@q5$E9@2pVi_H`l=T9d24VHL0J{p@ZIVqg)kMJA|-kF=)c z<_g*;L>yj7j;SLy7n}hw`g=`Om%$3DulkrfdSS_B|6JoQIkdOWsD>{s&R`d4FpuJ% z#O{oj&}ggFrzPU7ORX_YabFDltb509G37Y+OFTQ2q<#-DaSF!4#lgr8<_T(pj*IXC z_3bcHFB z7tK{=)mwC+g+yX-G_*rK)T?OWRwbSCI}bPc_iL?hg`zI!f1bbOMyOl9`H5+gTpi3i zwW6G@9H6!kR|LNPS+nay6zxB+rR+AMF!)WH!w3m)2a2`f2~nZQu+)t)=?C|X>#Mvq zFvU(@YU7QWaH?;T8H~fYVj?c;k`zv#aF^*2c>{De`a>NdCg6?uH+8M{KDpSs-{6U+ zD-IVoA9C7jRH|j|Y#wDHQGPZ_*kg-hF;%87?Dif6{g~Yvo3|2JHvhKMz0!=X7?0Y~ zXnog~6L_Ic+eg;GOMZf%0;-iPrkHr4el?j7e=I* z=dL&S$NcPkOExn?zxB-X?KlUWVzZSW+iF@ALD|deml)Ua0T*b0Xdj6P?0PtPF#7vV zN9AXFskOK49?MMDmt7j3$7y8!B(f47-}>{oJ!Z>GRTn3EjBq zQd}4`-7==|8demJ|7vlB?>?);H^$zqy8eOKI+=^A26xrl9fbm(Mk~jC>YeT3z1Itu z1Nsqh@QCmR{99N`x^`HYFXddwGIegsly^iBHmq#Ov>;0D%K1&J2`18u$3hA*H6Ucu z_qO-;fsSlX8c1KPF3AkL*AsYmILfuow~sRTmyF(0H1i=7#iG}ctKd|#7+3W zwq%cbN}K9__4$mvGD=XYa08u%ctMOgLA!H^OlW3?H9fWLMH`dH56sp-QcAw2Obk5N z_w$wuSL1S7Wdpj10x|Lc{BDkU!URHGa6Z5YF3U4B^Iv?HSPi$l@3RQcpJ$%Ie^N2K z>IzE_7^&vwYIo^5_xt+=D)9Qlm9^h#yhemy?gud*f4bUmfdtc)?3t4RYEMsN!Hv0WhZ*BX!~sWtRu(+Z;or8OfxaRa z+t2k;OnuYF3~MVk6IP|Hn;aI8nqKmkN@y&=eKUtbQWN{eS|EU`%5vUbGh8mrM!zjS zS$Nm-vIq^d(#BEF1N>a4tZ#VL9RPnZH-{cxb%CA$f$K90Dy%_{9Lu!#`bzXYxukf3 zQM%o-g!i%}YC;z`sAQy>o4jDRHR9Ga9dA6Ux?-o1sD)LJ|3mv~eEEA0pO}Nk-G{L} z>{ARJ3G0XlF#3C4h5Qz;`KJa+sKJk$3z;Qb`N`5RYWi0L^25HGH0s<*h1*Am!s6Gc zOueyr+hqAJdHrx1jRnpyJ4cfq;X^-7h?_FrCowSZ)O8mEqQfUn%d>~igRDkt8MOp0 z-^_F(ZyS59_*mVZdr1{%O8+{O5S`%h$2Y1{qsjg$;nyu)ZZ7_mY<-2tIOll>?LB|1 zt|h;&Oaa_DK>I@*JRNDOe-5DcOL(B7McXZAtvhDSLCxN*XI0{q(<05k>Mm~-rVYJ& zETTxh2d_NhBDHZ~qLU={4I8gJLoUrv&3rU_wv%{%J`bn{WY!a3lgCA2dmfmn0F>X| zN!ZiT%f^dO$kfw}$IRK;+=f@!RLJ^|qYiW-J>w!vyTf8qO>Vl&(eWvt4h4gIv13Pt zWS8{WZQK+YliCpOjt{+U3nWaqrZkER@mc4T%_bMrkj-CEPIW_TtAzVm;6L^M#?+Bi z4^88LJR~aw3{$>;Yk}OM3t7xCm_zqAi|t8`%4!w(oZbpuJ@oP(F1)WXiwbJPZzSS$ zTbBpX!|=Qq&o52d3S9SGw06lR;AH}CYWzRfK3Rwt@CBdTLvA%g{a{+{7kF-pCRlJ} zUVdROc2mwL%t-H+-}nKH$3+#m_#ke!5F1N~nUx#j{-T8=tx!>D{f1Fklv{jDq@;Y) zEHdrJNEWu(D|tLC>CZv3HZZ6qqGy3+cpusu7q{?u@!ZK(bGiK%U+Pxc4a!xgMEvye z{IRzo%GTC@ZdJV82vl~*NCVCmflpVe_i>!g*Czh>vKBnIBg-yERe;>OHgbIxaOVVi z(Lw>nkd+<8_T*bxl7AT#t(ojOC)VIOs7PrX)!OU0$a_oha-J^|W3ha7h~g2nskz(9 zXR;%t7%XLaUs)4VV%a685;$r(H&d7>t{5~{Fo@NWx+~jTS-QEp0aPF>#05)?LEPwi zUz(Hw3l>%qpZa-kY#HNJnvnkWZ+=gNJ{I_gD1lx+rq%$%?^JhzqHa>Ld@j7q${DUA zFaFv(qOvJ_+wZ(2@u;#t=gy<{s{uShP@QCNVFt5xuslWW_S45%_GXqnXN_#wimadFH4D$&vwF_=tsa9RGVffC zMz)LT-L9%w!(i}+$M8;cP=xq)CUd^GE_0h2B-&rXvb#_B1lr6xy7Soeu2GW{ev)E) zkQ~TRvW3~5$WEE91>OIiEVOsDO*_1yNHs&%@1W`z_VnheW4XcTW8Y92JCJ&IGX3{- z&m8irjgJbi{D;YY8?58uFU<6#nap75VwVhuwrePWbTPn}72*@bfzTX0Eql-81XJ&Y z21dv7W|v32D%e4|RW*{hlM_3N?k+F~BdC>w!wHdZbLCPw&f3)B*JeC)Ue|Cc_&?kISpid%NCp)A#w4 z%ndc#=Pct1IVX@d*LBkCre4Y_j{6wu?`%1GpWONBj0u$)dZrPZAf#*Ul35F5ut>}7 zJA+q-PX=7Gu03fNcu7;FioX+)@B_l?rb+o-LK2?K`akk?q=5R-+22k zmeqch7N-s0B2l+j!+=o>J4nMo^2^!}C}M~tA=iCS;&?1mjmfK`=Hj_jAB*ggs}iS4 z-~5=n8b+4JPH=Bz^7fQ9+-yB%S-HD{q9Etvl0}dbc*f{m@F#s%M-%?!E8b%cHq<4n zF_oM*)4VYV8w-?+7N+c>!*30=qAM#ukQE$E^S<9obOT5~VNc3@rdh$u4;LQ9 zV3CqP(FY4U(`x!F2Z+MK9yg7PTI6X$Bzzu#cee*yi z1BK}I=rD(JJyXvoMi6R>Zb0gA6}bl)<#)Pfh?czjkjUPokS)W6GblJ%a|bhHW<)fI6T->y@B~OvPZ#wIVBw9>1yh>?;Ko8=tsnlgmgq7jQ)NtsZVBg zF!8204JVveNtEHIG{2rLqmZ^bGVB^!@_aZQesww&($!63gU1)j#=eN@HWZW>;ptyi zIp6N%a?jE);e+e`Iba^0UQ1AOa}ZvH9u)_}8KMi` zb?^+=NFcio&Hxzwy_iOIyI*tH2kbC)bZF{Da&bLOkxCXeZgo$2II5Yf^%(AqI~0?Q zX+ZLQl68XqYe;f15ew}#`up!=basedK6_AePcNPs@NoJl{BJ2pz^ibW((@++3Aj5? zjzVv=@alVu!qnC59rx#&bne_+W^84v_M<1Odoot8OLRduHLDdhuv15R5P!_tq@I<7 zpmAY+i1waxT%}F5S7gNb+4_a|Z*oG)Iv=Loc<%CWzsdi~e)_NE7HHtDGU}n{V{p@) z;(y}lNSa4%9ZB=dy#1G>5Ghl-U@X{Y0Wvz)ikGWo#36$@>SgtUfOks~kR%so&BYB;15AfftRAz_w7lyY=zSmLFX!9;`8t!qFl>%sM95F2dK8i*_?s|x3UV6 zET;SIcD`pVT{H3%!OIC!JZvQF?83u)LMEaD*}Q&$E9J77^l;D9hg3}lXu&%sWXW$2 zpyB7f7#4-d*+SfHVeTg%(0hB&by_07c!eb1tu04z(8(Z$)utyp`@r^f4@)a~p`r;4 zW?=;t5J23i=(;$jS?LBt>zop#Lu8*lp0BZyqxqn^8F!m5A|mznT#$k(%oFAU&&saC^ zL?#22T*%VzTXC}j@Dt=vNfA+qn>6WQAUj>W6GG=4B?P0^>LQDao9AJFAfdR#Hw5OL z+E89cO^}W@6x^|Y#`55A$&-Hh=<)*J?MR)>gT`G(ZuhOyU88fqww>``s5^NLnvg7ItlY8>R1IP1id`v&in|qO`5*=@OoXNnif5x0Ae>{Uk^3RigKlI0F6K<_XmpenY*~KPn4Va^f z%`@xxF2E+sYF4amjP}ZN>kzx(FGZAzr`fH3$6E}SFUVD$F~ z9l>_fck_(~^U>-rclWL^tI+Z=fL;Tq!0i>}wmAN~qe(`>Df zy_~RB_QZ>p=Vr>l8u3bO%mh$|r!i=pU0tA-pzal7&m(ZMj?QWmgeNPQg`GDh9(904 zh=f0~e$;{15U7@%CZlaw*}&hAy`=Q)OWO0+L!;i`*p_A>UUQvBH(N<~O)a$S3YUgktAB*ed zwEz>*-XFJVE!~PUhdyb@O-%Y$^Hyb*?_Q27o_10O;yMN23J=4>oY@_mVAfDm7j|>g z>7BvgLvFFlT{XdM`Pp{t?a0;F*l z{a0JY`wNOFoSz82nt&hchbwI|f<0pi_25~K4fTDN0yH`Q{)z_CD=v1$Y^xj&+B;fc z-l^;!TYFDaJNObi+ze-pT?Gp5xm;_m>v%TTD){v>PKemILA^J&9GFer+&L&grkjSw zHbz`hyw6=R9SYcNx>zqVmPA}q#W$cTnGTn!(Bg(Oi+V`-!hI;gTJfVA&?V z%xJ{tYo+z#}w{}{z+yAMppdJn|7fUAypAbJcLR+0N@gUr&a+a&;$=VaE7Q#$3 z{%_~_tGt4PcJ=Ea*;cx*qX@hMJ1XfYO6iJA9If4L!)b9)Q0 zjD&NVgVW;r$JB#Ho?RAw{JhJF&HOg5zuJeaBkZWsm1~8|R#POu&c(tl*uxbi_=C~MG)7jm3A4i^3y&4Zx82kj<0 zP30n;dhd5MGbSR5Lv3{X7o_Izbdn%dd?yXAsb|&qg;Ez%tPM!}^5D+gSLzae|GR5~ zIkqOX!&3*uIY3PPkTya@aPnaE_nWRe-5x!BlE#gUNIFPdo6Ih66R=bW+ERUJ=y>C)a=5dN%uwUF=P2kR`?zEO{US`Xqo} zBp^uTf^d1WR8PAJGQw<=(T%Ho()tFhy&L(Oui6IcvC0ggAPsx~ZF@kQ8v$M+;S*~R zJ!cqc565PN-)ic02wEA$VNXc2>b2oF|M|T+pOM~uxG4-iVO7uXS6wA zv*&Fu{OEl=DFKU3y&)XS-Wx%G*O~0ddF`sGc_JM&p4KP86@ z>u*)QSGH)juCp!+k%NPYLlNIab3@Jlfwb+U6NY{4#Kqe8LKm~}Zx~XSCAGsWOm!@; zDcYGjK%q_mJLTlNh)P@=9K)tocsKa^0916WVA#K>kkMX|$gQTTX^6i4D@4}D9vBKI z^B2Q2yw9QR@z@faBhdP&e(h?lC3MWUdO^7*Q%SzKazP0+`LVHbfC1mt$!E&QoJD*E z_P>miuI`}=KP-D-OG*^!&PkU?>JqE7?B@|AEhvbv&L4mD!hUsYA2Z%qVv?+{1huO2 z#&rgmysA(Y?+cB(nN9d@1~~ao#StPWkXWYn4_;gmnq$48jr+>^b0fVj-#`guXN}b| zzWkBAovRsW?~RDw_*UVoeMP^2BQ$~V_fE~Ok8#SS=sX^)84SBmBJ94zpO4p-12xi= z*LT0C-Gn!vzKRPsBUZ8Gc(?Qz($0GxiT`}SD42I@>p63ScFl;KpR@X+g3gP4{w~O* ziZ?`hpl;OdEf!6(31iY2?-D2Kn^mKZW`lg+({6L`t+QqCLvi#qpel2#{2Eqxe-VQ@ zanGSk<`7Nf4{gBDi8BAkas(+oF<6+=woLf@4SKujD%RDDR!t*oKX1Qq?S*Ad-aDy_ zg?5u#-y}99MyoTwX#D9z=LSRYyPG8Tg-cO3G5iTlPXSA(>NWsY7N|D^9m5ft{EU9h zyJp)z2E?&P;x_wS?)f48nDr236m5%49n&QHWE9LaZGt|J9%Coi>A}mNq+gh_z7(L@ z!jO662maezk@PgLbm8h0^Mw3=2c7HFCRS+g_OM-r}?Sn#nsJDF3 zu}mJiNdQuZIx67VjkhLUlMQWGJgB&HcG5gwn=mQyH5R7lZ!(a4TY{T-r|x;qF0LN% zzX&;<@x%+s*Ym+h`+93O%*w&bYmrYG!-;1=*}s7eDR3$c$4yO$_UkRByqgreE2V0Q z*y9k-VC#nV{QBU8$5YBg@pp^i${ORCI(SB`r+>@*S>3+%%;>f+g_G4r`+H=o4>=d~ z%mS7!U$Ac|7JDzxApuAoipfcSIXz-8f~EBv!)#ALs>kh_n002nZwGd)-2Bh6R_}p% zrwmLumLH-9=&UGpf!yw-T6%fTk`^|PKGP3AZhga{L-6yiG%tfZ#R^)S>kN?!w-7H* z27Y&F1YVzSnp`@4do*!QNA>4-w{QeRzQWBX)I-wX;Y2|H2zsxup*hExvHfyc;M3{? zRcXEjK6`VVy1ariZJj%FZTiTufHsSlcgh8cim=w9BZOH2oDNh>Yw45bMuC~1(mX9h zKY+8&Em(33oOOzJE}*8xosWm>L=$I_8KGA9HHcApOKxfkVXTq>*H=1evo zV<@L$X5nOIZVs|I;11)Hk5N774$sHWpOeZTTduypLF;yd4ch-P(&E*VhzE-ahBQGp zA@(qPPdk{avnj;I7Up(J*Vbtn3Vigwuzbf)#gd__NCsZ%2Rk8;m#Viv@QVj2 z2n*X=xO$#C#q|>U$zrQDkxmC|b`M|tHz-Q0Fc9)1wdO1YK3F3w54wTbk zW>Q#G&f>uz^~>{Pk{6c7-u3%+CaR>~6*tneHdWG1 z6Dl2DC_zW-6FKXYh$*>nzr}pW86;f)E9yWrcoF?sXZOaA(xt06N$8YRT%e|w_FNVq zzHMcO5WrQd*q>?==Bel@s5FbbqIAg^;k{iYNxTOiwKqcqFolCauOllb;E1(>IGyVD zv>o;6E>~*3q%j(o-g=LuhLeVkOlQHWmVS> z#|*|wh=X~jLO3o~cA%B!ae5WV+c~4VCitq?Yt_ZYZLT%%u_SEMs1(9-dYXr7l{BOK z`@|VncO%6zkvGm)$`Be)U(z91A$}DHy{me&d99xu>aJDKk$4D|a76rQ8v}b4P9Kc^ z-em1TQczL&bzA=6S+>0BuoHzZ$J3#((h;WoRLabA)pfDQLW-&FmnrjkgI*amR55~f zzp5|KJX=;eHH%&*HnPWNbrsAzrRSVLe*Uph58MuCye0-J`OYMb<-g3#GJnY>n$Ig9 zg`)*|XkgOi|4Awk*EV-6=f(cprFRZcl9!tSP6ba2-y>IEw=~jG7!F}l(`=l98}+D% zq`|}gD?R7!%2K@XCJy%E9BQG=CVB=#w8&`TCvOkaY#vGmOv;l$;P|kQE&o^@zmBnc zojxyfH!dvMI(0<3{9RZnN+9wkJe%>9?PX>KIz=3!y8a0;ol!zTspd5CH2zN3>hmU5 z>xZ_N8_MCf%n@WpE01w!~k|GizL9(t8cL>7%b(f#A z@Lz~i+mS~fI=F@YJAyU53JJ0^ha@R1+P0lX4~j1!cJ>zb4m_uv`HeF%tKw?zRDS2* zHwsiY5n5Z3Q(u3-Fj7-x`Ea;c?R$_S#M#uy9O}e}XegH@^hnK`6)WBNk#w!4Z6OX< zV-FhkcP1}>?WJ(mZULPTMH|q?7eJ+)(*6?}@1EZ0B)Ey`?z{i1EEX3^y1i?2*KW{p zuk(AS;>1%mkX+#cwgaenhhOC^PP8YMBCUR(SwaXC3T3bqg|!?zdC3qV%4-i{aZYjO zKJlnyu?c@t>YyImLL0wOP{-hPM<*o#)fl$y@gbJpZ4`&R>^Ycs%9U&lIm+Y!aZBDa z-lqMtKYiY4b2nOQb%S+|Gf*c`|K12hpH8%%H@m*ljgf^H?MZ|!ja!fR)YiUa;8EnA z){D$lQBn8i_8klBRGGfOb<=r382s~R+a1ydqrYEMid?t97S?6ow{%o~qV?6jjz^z$ z@?K(ch`^Nk{VtmQN$~Q+HMKS@aoLVM$Hz`AW@ex*(C?Sko$aT&3+@(r51tstXqW;X zXo4l5N!$(622K3{Sg9?<#027G#UsQG@+k!QPJWF?kk_iS{(8pNuZZohFpn@te96{f z>hrJDBSbs+ggJ-Ic9JNk`DkV*RyNG`(vO46~?|TjF z;;CanYM`hB-sKQ#1hE4xZV^Wy6|Y+(>s+GPCrKU9?001YwJMLfDnP{{P)4tqo#V4h z&<#+w5oFT5K>Zx{ZG44IW8o?L)y{Z3d3SUi}WX`TA9JBMQXeSvl%Uc=vSY4^pL~{dCe^4!u%6EP5aw)!^`HD^yuj3 z3wV;h7EwOBKj&adX06pi-x{jXfxFsH+54av@vs}CHE@;!6$1X874{W%llQ_Nb5vwT z5tX4+L7npp`)^eli5uLGj34|dA-%0PsEHM zfR`VxH(AP&hwkIy`Pxz6t#KUlE*49w@`7GWNT3 zn*&L9LTcJ2jnCdC7WXP=b8Ts2!GGv(r(1EjOWzWvY8wQWi`B=ABwgP7oj^2guPK8=hK! z#sstwOjn2C$7KG+q?plpSA=(Ge`4E=UtqreaQG*K>(y$E)82!5rxd#b z?_nSZhzF!wpHV{o689fTZDe5e&n_EULn9r2(iJG;UaSn$iR$upuSqsDVP8ojPJG}+ zkuWf+v{w{HYBUw?STn3Wy}rmM&^NwtsMrDFBjFs;{wLuK5O7_8`@@Aty-aR@x@;sv zLWcQB4nsq9V`W%1NF~Fz?OE0e5*wk@8hqyA?`k@UxB7V*q95BZ^2u>P|{(jvIjN*ud zv3nUS!vZeUE-mEW>*ff;<@X7Hf+z3#eiT9uUVbPVA7|3-EPH&E-f**k`MaDOyA&Kb zHa_zEUIcD2i?M{Dy6toyOjC%13ou%Ec;aI?DUaV7tK9(m z(Kg-bhx&Xgwlk)Ko>q94IqB9NHRN*Uue(Y%v~LXP5wm-3O_%<9PPUw>|JWvpx9~b6KL15bHj~z?;Q$vAbuj@<(shR(gO*$W5J0% z9O2sLx*1=;(2kkjBdLJW=T6$vg$ZN)6-YFX$YHAB}g(nvO$-6u=X7_!b zncrQyzPtG=U+>?kA6Y4Bk7Ti7q_1PQQ;r}WekVjTYl0D=8`fJeHZSz1id0T`o@xfQ zD*na+mugzXgJt--LiUPPmAIY>^oLXp20RPsa#H$QJ6u}?nkA$JTk0O%V?}a9GX^tX+BGCiw>ZeT@*Kx|LR(xkT5kRGoP_BGu_uykgz=4?wr_p})mDWjaE5)axraoQPv|1Zlhma4 zTIgxy+z~}3*6(+no}HH!fLT>H5nOMNcvJkdM*kXiFu~JuGs+ zi?By2FT9G9nRb3#et3B4rkuRJ+g;zR#NRBO)kx`Js@Whc_Jr=s7~CF0E&#SvF)B zN?wAcv<=vJaM&wTn|Qztms2&|cAy#UA7|TDm#OsG3b~0D01|fp`u{kDN&h0oNVCNljB$g+$KM(s1_z4 zmJGe?z9uQPP@NT6y@Yd3zE)A_ciaaK0%iF;Vv(CRu9M5OSV+DGzMplHnVt)*xxtX9 ziC^2#UqBagqK3yRun_d&BOt27Rx$rrf7R{x&4Pb_^(}M)azVfLnfj!XbM!Yz27&Yn z`k)1i1?{n@dV(?hua$c75*IoQN#U#rW@kZAl%vD53_`es}Wz`Rq6-30VNv2ld2 zs6SZ@XB1OVE#p%gk?@u43$%4~UY3m=Zv`Cf(gk}}?AiF8Z1E>Q1jMq?st0~=nc9uT zu9{?;)$99wUTCX-TrR7_D+jp;o(_%nhxUnx{yia$gqbrU6f;}de-TvpJjs@yH-5@C zbnvjzVe&vNg3C1hSVS;2yJi&aThi44)`yev^N%mZmFBeuev8_;(6?+hH72KN8as17P&`n@-p8HX8ejLAE&Z z_>Ex>Vi|V>%_ioG*HIeM=<7zl`mzvpsaMT^Yoe0Wv1&YjrA$jU2u^<-PB7 zQE{hgN-J-{KX_RSec@@GZ*^D4D7t)eK)8%2#M0x0#&`lx;#yI!@&wskYUcc^xn3`h zSV9r14(#~m!KRy*PvmTzU@kC(2gCeU^!e3Bl-U(V+nw~cR6Y2Zx~)oEt_$Yz_iQaX z+pj6OnZlf$pjL=ayizm!Kr^5;KlwSy*NtWK2Zawi>YQ|U!JYNefYMy7NvZ_wgF z5kFxXD^;@>YEzqFsH~lX06!w>H(6OjnY0}#?iHhvM=wkLUcEm*S>OxHhSVnx=xnVQ zmf;0`4mJo;&9Ir9EkYsTL{4K*etoehNlYIce0S#+Z~{@;+!K0`pF(0(0=?&8_}`@&QW$^oclQ#3SVA+IvMYx*Kk# z%qF#8O(ZA?s(F$8KSnJ-W)Rc%ef~)KQ`b)NXb;+YfB)nqbYlU*G2vkafrh4{Dm_BMt`pgn8Z0ccyHYFPWV}WnO#{pdcQ?sSM`To`h@mpmV5I9 zN#Ny&VhWXUvNmJlyl)}JF^d!u_sPyv%Qg4Aq8gDfneWvN?>I2;l$ZcQtrdu;9YT!* zg#}KQGu}f(3Z^2RUZ=iQjXijQ)LcGBL*W0Zg#SLb8s)~F>yI+3s-qlhGKVL8ny-A~ zhE>a;O;I%Ulvo$z3Ap-5d%q)fg#WA<$JD_yTues{fYINJiBchL&O%1H+8s3~<-_f6 zAvaIwFTQl>L@_=(+A9pG9N^`LVp1hF_nS>qt|;|b9zvFWwSxZfmsClAnY`3|akkFG z-}ixQIO4GOIaCr6*kw;OXQ+6 z@m3W8AnTi{Fi0XIsUYWU<^XX)@Wh|AYPe%fy~XH)_NFItU-(f~y_j@d7GJa5B|2G` z%`ZXn;5(SQIw1m*TGoY2q2#nVo*mj#UkMg_B}0O^qG458g3!)E`LbWsp|Zf~Yk|-* zKcFerI96`_vDF^Cn}lA_H(P!Pe@_|WGCKQ_-N z$)9zy$wH$Xy+xC^#joawc|E}gYE0w`jpHrD2FfUi>l^T5IE?uY^vjq)Xq-K}KJ zyIHV7Df-HY)Caj!omC3AnuBD_%pr*Q`ULL73-@m=ObJN`NC6h>-vQ zq7yC7ui@rJ+@WNBD>bXuV>EHk0{w0r(M`;^+ofJArQ%XJ$^fq3$^|&&0Vv4HhupS1 z+V>R_DnFl7Mpf6zmfqi%&CJoJ-BCuAob9Y!Ecv*>|4u%0fdEWy z1s+?GnaXf$+?-YMM%lrT7&|MbH9^1LgS-NB#iOex@RD-6 zdO$#waSK0Od%=%@L+ar9&%FX3_5Qacs1CJc|3dVdt?G#YWWg)UJ~@F3pLQ~%w;8-q z9|Q@YJw6swOY&y$9G@D?O6mC&mk)y@W>l&yw~Ynm8^!{+v(&ek&7x z*zPC#bh8%raEmV@&)BhXXtd$CW{hL%r^WP-V8~A zQX#ot9cnlL?7Va=CeGRolbZFiwYxBC|H8|70kX~0*4aA5>Rx2G8_ks%H-XVS7A)xt zs>ftZTm=zkQE_-*_T7*l>S9>FH9eQVT~RllXPO}}(36zxnst$-f2IQRMod8+fs+&L zw4LL!d+tHIEoxFc^&0VbWYzbQT%Sort~E7lWZCm{Bt`_`at;tr7YG82;BADP+=Vwe z?}H|*GjH7M3fZ>AE*p{^8?9&|2`pXUz*I1UL&^i}oRfXd^P!)lQ~7G~R;0pOlLm!a zhz6_A2Qz3u$i2^fuWtlDR{*GmW}ZBJ2pio-VZk;tqhp1M|E*Ey#Vq23a(Ck{vB-mx z*pNHF9G}GAum*M~%-z%;G*dAXp_IgQ4Ph*(AG`y?$e1D>b=iyV7Th>B%!{uyjEG&c(aZ$7jYWAC)&hIGK z0z)yR)0S7RU`Z@>IN#(BpT|3&8qo`|FP%(J>&LyJi6O50qoba|Ms~HyobPPErbYTA zMKiMYmz$@VELkXk@Q^9Q))tZ1o9ZC$(UbJ!!>S;;5^)wT&rmBL(^aXS2g%0wO$W6_ zH8h+db}p_qPxPF#(w% zc?)}2bEp~ORHcNxl3v#PzSDF&{mM+NO6Bi_)ol%%Mm^=Cky6_V<`a|A!bpN^pHK6x zmpg}L$5>)mk0HfYl_`EkaA3Zn-Ztqtu*XgzIsf@m><;j#Gy+)ePdkG%lzZw%O*VJx z+?8?{lis*1{mu(BMD>$=5BD_V{~t^oX^+DzH}}6OSu+M&{@ftKHNsEQqj**|xvk?I^(K`G5`6ud zp)^=tQpeikS@Sg>->?s~2DAR&lbk6k_lRCFwx~YRyZPONp;s{7AIv)i#9-rTd#a(x z84H>Myl_L9m(;0StU2_BL}62)+uN{~XagsVjrIZGMq%DH30l1Oq7EM)A4j0dxDmpItrl0tz~{v#f^M_v#>xP#H(+t&y8lH7iZtD8gIF0PW}QwF&U z)dx{3l`_+ylqs}?RKneRhxWCyu47wIIF1_2Kss9itnHH64X{Qe+wui2y_yKxqbos;z!bE{;_#7?D=C5KA8 zi_`6go0mwdQq60m-c@A{*}6jCE!ljql})dX`NeQOgBva)jAQEH8GckBF#tw?FQy_T zRI(uoDCK5NQ-AERM5Dt8!>^(9#;+k89l8;9MSkGrhhkb}w_AvNuI+rTe~4H9X=-e{ zVFf!?wTn{aYL8QC51urbcPgo`P&l}HWy_cPjpST z(;;bC-}G@dVa<6Y8<-ZyIs!v?vZHV1_$E2ADV^OR*O|sPA}~6csg`K`C>tJ9#XKPo z25>?B@k9RGc4IGuT~JoUs*_V}>y~)lVi=dAL;J?u@cB!@8vM&t#OlX_I!F^vx6CMs zmAd}yV&eDEl40`m6LCb0bn0?}6GZf4^kCj8x3V1!3hpofZT=Y3QTWWQ3qr%cAU|2b z?J@gU@-eOXr7)p<*T6;_5f{v!n4Q%P*RgmxD8I2s_c;UQ<7mNys=Xj;TFP;x6t<@K z47A0|vJ8hPstEcBQNdXNqrYEMlYF&i4^}yZR+Td~rY}2%PN$7=hu52O-Asp6U+OfF zI~Em^Sbl$QDD%AKV@@P%hj1rVW#a2NXq17Owk0oGc^jSpyQUS?=}01n2Pr{pp&k~l zPQv_%GwQ-6G<{(--}V&4yyV(xak5lKThSMqF4HzfwdO_nRrVpk7i9+ioE*$d-4C{# zE~{k?@e$%*dtFQO_<+@q{z;9xRjd^YkrlZONZWO?bG3z>N{V%Q|0x;bvg6xoJsgTo z`mB*#ZcY3x4cU)aA)VVfHi73uWZ`{~5UF$uYx_tC{=wB~+Js};pK^N?g++TSG(ujK zXR8i5>Wv{O!M88~rv!ga*vhQZah2hfD=)8oczwfc>fRh(>#c_h<$30eqkJ2V=dnX% zLCNVUv?yMFg4K921zG#OxCIkd%pWs;MyVN=#pw~>CsJNO0yyMNKvIku48hb#@y-|J zYK-1@I&M)9chhfQWF}svnjV*H>)C|qP+xEkk$1Op0SVrS8>kGfZte?X7hl{vysxcj zYu8q^MGnlGAACSQm$@!{iMU=JVg{~=;C|@r01AA7kMQKn=lsE3wjdL@-x=*O(Jl1w+H#9~NU0miijaRUK zh&)K~29VTPK4CM2_t7wGDh{cpl854&{>na2|*EF()C71_?%`b{BGm`M_(kyr~c5nVMSX zdsV@_QxQy0kUsAWHAiUBGsaPZc}d+q>toXrviXlgeiJ#|Psw{?A3eTp*l^KQYkF6b zT`Fs8a{6)$QU&(CG2{&yg`GK$f?D#vQ2s1u^P-@j!wBXPaljEmfBsuo?#1o*qi=9x zBt!GCbydH~e^cd>OL8mejLC_lzSul_-Rf9KP*#yQy+MTJ=r#>H*Uy<@>};=_le`>z z;(v>N72PTMHkfznJhp|x*9P|?QsQAm?5xSqA@8ZNh>8nm^X@NnR=>5LrTAUW&nW+a zL((8D$@6>b#$C|~yh;qr0NjccNxvT3Jg-vb-Q<7`LW4cMzi;k$6{jCGsCWXFB%xxa&=>P z?~%`l`OC~4siDi-eL5jgZ?cSm{vHjMTm*@k@&MJ|#R-bAwx3%LG;X^-=yA>P$@t7# zL#}^^r9;`VL;Ed33yo<{pFxPCvn>o1`EhY0s_=@0KHF0~7FFUB$D=++#ClH}b#yT5 zc_#8h_AUmcdfJ0Or3f$wI8xN9O-C3Wts9_NnlK&E2Nh)47kk$C)Q?FuB|eOjt+N1^ zbva1Y%nG>VoPmE8=Ir2%a5g=HaWFil|0e2ikyfBf)Q=tQM(uRnrI-lDI2n8sYAdf$ z`6D#qlTG-)0Vyj1>{NSD@db9($!8STM=bLo$z$xg%{5Dl9dhnQPqdQ^jj+$Fn4@SD z>s?a@cS<;NHz+qo_^3$Gs2@cX-?-HiU0JrzKU*@mBKYO!{X)8<0ylDLXgE$`*^kQEYU;R(Ee-}0nKT}NBNQ4e`3)=s1q&Eh2EdtwsmKpOIQ}p3bIa?Ua0fGqr$XBcqi-<>UT1a>W63<{#qr`#jBg>P^gm_9It#Ju16AuUz^9Om^kHjRA$`~Eui z+*Lop5LpZGyC58=wm&Tb6K{4Gn%X<)4n8GZMqOMav$KA3&Ycx!{`Jdj4|#~CtGT%_ z!hm>z?H*mQj;o(H_i)8@ceCj$XGw!$$ChEt*X!-K9SILYWI%Kj;cqzis`q-am^D^# z&HVV7cVVE}HFF!Gp)F!awx8-vv&%!uHefa=CAPAGxt)A zFP(1Q>}==hY-#HWfx2-E3c`;%cv=PMntZk-lt_+iRgqWljFyZ)sNb1V=Js=Eek#1* z6IXUsD0y~Qrp-{EvoYIXSIo%yy4(Vv={VCG31s>!w;|gXT=~n~4;kT(Jv_*N@BscF z_=$JK1{nRl87!?>P_k)ni`O!4l_kJHPHc4U$?XL70S<@j=9&g5Uk#2$_AI%#^dN9| z+gFOAxl7fXSLV??%0(}90yNMNE=mNg8q7O&LHB|>9peH6q{K7Y1OeUWLW+I5%WM<% z+Hfls9!c+Qw5HN~iGL<;)}-h((RCAJFt*&K`7Zkn5;8rTxKcQ*w>f=FX>`ulH@g23;hbTNx5vG6mwT+{Y3^fQlPA41fj6fSxZ1FWJ|@5)`8V z6v!-dMEi4xuWao-4UaTx=wekVnV*`(*Gz}YJ6T)1TU#USa;`jZ^VvYW5DM`erply}hPlB|=d+#W&p*P>y&lWbpieMr zbrl+wlc%r00h9q~kl_Gv_Yy!bHl9|G+4y$AB8Nb3Ebo1{sG9)(m0GVRGONIkVUMom z3f%yxRxY+s3lnf6c%E!PEQhz0T})K^UOQ4qcg0g5CVq+6_bFLce;iCX`9#dMAVl5_ z3IQ0yh_6%jBe@9^6axg#d?-ja+P%TuAP0oAh$M6w4ecMG-37#zbwRiea7s$Wv8ohV3MLKet5Hh zZfW`Y_9RY_Sh>gd4+g5he*pei*&6}iEf_-4pLa}L=t{MnNuQJ^5o@9su&P<=y)6N| z5?Y3qDIi&d5+V;;YymK5gvzcMbKC*tm4fM9#U)|CxdZQhZ!-8v|SUgsmVo;0u9c-#NO1JO@55#A5hG zT_Wv;e|^=KdF}Z%jHS{F2Xwj52C2O1GISDY0#@ITcL8U99ATW2diG=Ub6r#8mh10c zS<%ElL7&Ay9cr4Hh=V6TopLKU12Gtxz+AY6|9@W}Ld}MS>XLYs>h=mAL@#vhw^tdq zpzy3bciZ_mK+ICC@{xtIZsHdImHH*Yh!JFdnnS*Dc9bU5-@$y^H>|Fww z=v8$^Vwue6<~nQJ>HMhj zD^}fAN_+8TmJ5^P!~+*wzM9I;VLaG=KAG`sUh2+WlWRk-tD{Oe^*3Vn$>bkKSgz%% zpe79qEsHvj z2_w~HALLkID?*l}m=4v#h7(%lMM1NSg3PLc*J>prnY*v!a!cK zCB%jO4>CE3lft~9#}%pn2S{{uy$X-MO4_>V<=8^i^B5&tnG;S7{XcBkH7joIV{E?D zO>-yu7OXKRHJmh}**H3EWVc>#PfF%-Pm0sL=AgdgR_-*{Z+;IOj)ovX6@_mxypfh& zNj5L0%zQ)A*84BcGhu`8@jo*K{^QXC{BJ1H{~L4vC12T`g5*wMp2va4NAH|%C7Z~$ zUJbR?bFFD=_oPIL-KzSIrHZHM_GonDA&cSc(*?^b&sVKEL^W@#y&FKilrY8Fuk;4Y zJ9Vs^TUf$ffZ%tB-yzTFR1P?Y8S}*K%?0z$+yUJep1gDJ#X`^*+W9Ft`56*^rV3f* zGz`>DtAZ5gU_%Q-DcarP>i&JWcS`h)*$S8!RyE<0LxEHNBcx;EVD$HkZd18WuF-eY z(tvtmQz`U<0*A?H^n(1HYYthyp2>~$O7QakkG1!X=lcEr$H|^iB$Dhcd#AGZ3K7YC z+I!FJks?_UN+D#Ek-c{$gpjhbMUqkcuE*1QzTV&0@Amet&-;(J$Mw2hu5-FO*SXGh z&biKk@6uD1$rhB-F&U?D9!XD(MOE~kHbh^Sn&G`N>eSRXmNyROAvi-EjT{V|j1D`O zf+oQybSDP{nsW80T~}mUFQOwWBpg%}n0{38bD_gMcQAu?H;|G(!dLoJvK@ZjdoA9t zyRVkX9f?&;}6<=-sSZoodO0M*ujgQRn5EBz89yLsJ&&H)IGG7+0$j`98%^3`EKPYh&-v6=x!Bb z#jh4AT_I20IW3}}h%)h%TQFzow$pE#`3`|dr>`6lK9IWz6`~A&0(cIIgVCSE10&aH zFYI30)S6zJ*ut}7b(%(zPSvH~BYpHZ_S1H&G*}!CPolMDY$$4>*Gz_;`81KHE>>;sjd$=;=a%YTZPq*AIKFtFLXb zMy6<~l*3+_NxDC5KOZ!OAD}R!b)O_^`BuE&Sc*)3ve#@$(PpHJ4zBwh5Gy0YR12>;bHv0xT!(xdz;$9~{zWfk zu88J7crhz=S`N9G)h=66kC--%A}z%CefA(*fnWNOUeZ}wTU3x5R@4!QRU#`}=uhHS zx|!vf=W!9uA)Z0s4tlDB@bb&;I7b;m>m+g0yP*$rz7KB)7+#{UR>{nJa-L@Cp`$4m z0d0X0#e#YE%8MTqiz6l}hVoKvW1$=b#>-zrPrnw(>QlQg&4r8RxdBPkDL1!cfI!F= z;_A=kiGy#gXyWNm)UbEI3!6DaHn@P^N@sS0p`E6$OjKPMUM($D*q&Q;UCnR-#;dwJ#%%E@>qLBqcyN=+!GA{==Bv9}e6VzzxBoGBlOnK-< zl9k=0SG?kt;>k&6(63X8YwrD{_+F*un|h?Qmk-8}u4O1xa-E{8P;%%o-brb;fA%Db zJ4L%RpYeGbWbR=f(}zaKEP&CULxuA}r^qdr%D+AyH-#;{&g}bD#)WaSQl8}0FZ!hy zjoyI8;ZR**_RbN|9(qt8(E6EdLb@aL3v$ep*X!iN1Lxkz7tcel<`Lt84tBuCvf3{? zz8{S{;mtM3uGv>oF;YzHX-Ce_#{mML3P@PMJKgD&@ci4FP2TKlT9?yyLW;zs2Ja=O zO*{*FswE#c6WjS+^fQ4aXQWIobj8QAJ|qtYM?n4MGZUr@t3eENIW=R2x8CPWn~w-; z6-*S9(fAbf;6FH(QTyQ#DC-Zcc4r>?1;qmiE=s;~E z6k|03PnKMdnkIp#Fo-3_cO0I6%w=M-skL>}V^+gdBIV~1r}}E}L)m(rPQuWpPb>8O zo1{m2{B=}k8HKwoxNE)7h*_jxT5R*Yee-gT8>7u@K`{{y=Lvmk{bD?Y^06@5;r>&J zpWjDhPa|*j=x>BTQiZjwXA zakb`%I!aUWbg_ao&qlC3T&^f}TuhATV#Rn-Tl1}%uW*r)(>@t0Yz}=F{)xM+Xc0UR zKs@olAaoc4Kbd+8*e;RA^iq4MBo?H8n`BVWcns!WzNUWp zr#v~kdxszg|2lI{73L3*#Zgn(_KlovpO$tEg$U))I%QPp!58?#_<#+J1LENGc+35r z)znceXQaem{DOj0noft|fq02cUAoZ)9TWR@p1zM+0XGjp`tilnprsa1oIc^Ltb|K< zklq|hD&NPB;c4ZP%Z1Kds$d>M2+0uC9CtJ@JM8xgI=Y=O>m?BRo)gWq^+U${jOlCF z(&iUb)4!XcJqu34ZMAPjjdmj}T}$L9*&6pfKdH*?{g$Z;D$T z!%6sY#*&0+aP%RDE&i~8^y@Ss;@Hz#r_uJFgv(fO1S1R0wAXUTiR$o$HD)(|@A3$< zJyQ0?3ZVWx z-|=0!*(kKjupAwm(e$hPyvz)HEBu^0B7XqX0S938=hBq7e1Bjh_UxP7447xT+>5E` z^5S` zSdVWNr<4wqIRR;N|`MXjuBe@dbD*jc?Gfvu#U)s z0kr)NH~^zRm#UT=|7411sb#Nx3Vh!ZAWI-;QVSAQwi8)$Gcc%sbeji@!_T`LVa;W1 zXYP)L=wT1$A*yl%koav0&td$ZZ1DiK zmLXmPEM5|(wVO+w%q?VIm+Ea+bGPY4q73i#m^xoj&#vFQG&8bI@zsIw!xxsZmsz(d zBx15M(XV~&k(9%QGR`o}_KE)kq`&hE^CIH}HB#c;BL3QhgsG4k$7kiiJtk-$S9tl> zTQ1Jm4u>F#DyhMeBQdBb)>H|))mQThfd>Q-d2KxbU^$VB#1s3{rQIDarm5M7ivCu<-%t@j@?z4@rTTO z^tT)NAGMm-yRp)KIRwe*O6BW${0FhmG4rvT$b0H!tT98`GP~@XJ^E-+c z#PO>L_{e7WLU$_63)<1r=K7Z!Y?D{WvnU~yFb}DN?*qN`*N(Xt4^c7uJPLzW`W;@} zx1SmRaSm21R%C74#6P$h9ZBqY2ov5gX4=&&C0keA2=scn1gV*5c*#xjmKEdV2&otsiPkQa$MlbO%yoPfvqf(E% z>|SFJxRxikzTGrE6N07W|3&Lb7m1>-AOBb`%|+R~bkf%_U+JCSXLhNFekWSw#ShKe*gusa^1V&IiWjS8J{JP#&uKfGN5%foeju%Y;zwmLs^ zw)(g^omSJiMv}p>Yh{bq5~C!{Jm2A62lEg@Tm~Sg-wM1Oc7)dog~#hpTDB>;Xh#%f zuU`L`7P9Dg%1ly+4koRxX>61>lFg0uty%XyDX*g{At%i=U#) z&o$5M$Va-bp#V;>kLg3BeRB^ustzlnoWKk8FHljgR@q9e3#8L1mrkVx^?#V+7oYF$ zKPCF*y7CR!F7*{a3Jw(p&$n*#^Xg@4R!Tv9RVf;4ETtyjT0Zt~ihcauVj|rJ<{=bh zForo=?mHF!Cp*iZds~Z6u7H*$jlJqg0E2_Bl+PVg*hZ`9x$E)oirxgq3@a7C#c9Jt~WHcIYsfnKAOS>%6mlLr+om?|AmS5ItHfoKIh{+g?julEA}YY zqZ?uQXKAFpn3dLjvOirMI)o^q16kb+OXHTjn%U%Ue!4P83AL_n)q4~g8!L+MCLcWj z^AIYkx;cY_h5ME$$Uix$r_+xlwyk`qHoqp!jXCk{JJmDK0;WEgeyPm2#Zh>G)0|GJ z(z0zk$JGD$lWbw~Id1&Eo(Ee~HpwhzZ^~I53`jbsd?Dth9ME$?Nmn z261{_^57#TJHP}&Y{3mQC4=w*HyUt3J>kPUU$rZwx9@(7I~SsS-qn~3wqaAewdsS8 zqZ_sy85H5WIiw)PbF+%aCU47**-eC+U$I7^dK1j=>LX;2SIUxR5 z@G!3FByN7o+ZTE5 zlIRSoftRdY7d+AkG8fU?JnKq^B|6N{xAJQ;WMNjd>uc*NuOE>xLA;_u zl)=ya0}xIejQ(7D@PD=h{Y+3TdCNQIDV?+-B=6MI-&RfL7B#vu*Cc5e0v3nEV{d)K zeIaBzm1@H-PwOsQ0;$a?%1+zc>1S!F#F;rWO<*2^vIbd%=GGn{GY*R29XQNR=w%6J z{;=cVN#ZYi&tZ-1)sk$TSFFHh`Ec*&lu6pwJ8DEVeW@4i(zsL~1>eqCZ+TLyVpSfB zqyh^xmIxh1G6^z*9)ti_`5}F1bj$)6{W(-IAMTAeD#R}JFzh@vc$A>xIKZCm?^hHX zC7WmGcFp!GSR4*jx6{PWywfz;qkWr~IOQkFSY-v;o0iWK`+d{!Fz%`Z8VkV#Y-J5I zHaCFh(K`R{_n6RTTDG>|Z&DM@lqArXuiDJo>Z3HQ+}r+eL2Y)CuVG0JgYNS}KrXEH zoW<--%XmeHadLym2Ewu;yt|RkwY;{FLc&GlU>~kS@8Ms;WzeR%v$CEg);39PX8$aV zC}+4*jQ87!wuR4c(=3(8&4+NImNXy@JQ#}8n>~yAqnGf9fR&dx)pgp0(3FK0r|os< zY(uykgO`rrB({OU%l^al!%6FB1PO98-!mn=uApl2&E%44qD;&4J71W2=a4L?YwJW5 zOW8(fvKRcele<)i%Y!xeuUKwI#D12Fn?xtc7& zOgg>sGOcQpIcwK}H>SZFa4Gqofio|o3wKh3FuvTSCKE#b>*J=7-x?L741OMO zyT8kj?>UzwyF>YeL>c!7&k{eHVJR1x`~WSHviV4tL&%2U@_PkO-lJ;vPS*Ic4rC6Rxx$WYE6 z{;tCA8i#fkh5M6sU4EGhB4XWyVYOS7QaEA{B;_OJC9ETztxOE+*L^A--Cub*K#p=W zIO%z6>@07;&AzK-?>Q0 zEQ%YgY}GtcyW_zh{b@4rjhj-?Vsl6AIy~2eUJSfr=aOeFp3}U?iO|))~mU zr*!?9r3_2^d#5wA4|6Ai$5+*OWdLT@S}puq{*M~a$5)IcxfCF z1F4((w++MDaqnr!0gQP_9ehLE?0^9<`g53IU*ZZ(nMgWaxswvYzhjN91#Vw+CAH>u zGgj~sbtQ)M3>+p(gArfrltoNg;a{`zy#|zpFIojugNiV2;S;STy;nL9<{{`K@U+{| z9hj2G<#tc%)(o-^SM&>>{*_DA#pSkUY)H1e>Q*qkL3|Yn2LlKw_?wu&bd~Q!=C#XH;`WAGt}HB; zB|pd!>ampNR9!cdla!beOo5V6;6Sn86CZ!uZg00P-0j_HRwE|TMr1jAt(aO;-_njD zsz((3u@F4KHs+R~DhY^A!9Bo0EdgiwN%Ou#ezm5`@AjfLtusXmKUQCg?z`#COhdhAM zpG%d@DK~!YVJDL$?{#vl)8(mROo4-ES1ucN+%0&f6Q@FP2-Yo@)Tr{jOymZU1%{t4Nb zSV!c+;8>0SU6krKEo1U)Qqfz_8Tr38#`?a-u9_C%yz|!1%&`#90)6r*{rgoX%0xMn zkD#RI9jeuPUKX0Sf}G;Eras)df0sC6Q|}dE2|-JNG7gS*wvNZX&6DoFKw(Vlv3OXx zy^9^X25E1bBf46e=xBYu+BnXqDf~9w$3Yj)_T$yK?^2I*+~yaF%tcbpd9D{dW%{Y> z{3b^DBgo9eJfyx4)4tX8mz&Rj?;-E})8-L(jy?aV!eHO#_GembwFYhZv~O!(P!d7c z5cuZ9jG$<1WpF9)USJo{Jiiuz#=K6_+OBI~qi%KG$tWoa%tP=90KW-vlv}_R1XP^# zgpqT975~h8hO?XPn=E^|9$$^UE~u$4;c?<4b87`9U0o zs@xVUi$YdC>4gSP(iIBH^l1$!xnzcU-jL0Nc}V>LrUM4R=+8A%#iJcn-5^a~!lkJc z#{fLCKx)zIt27z!`P&qhxA-%U3L>zRH;!UIKL6^qY`g~2a#l*jn;8vUT!YO7_E(lv zo`DsB2b81A;8w1-4pzoS=1%7O`M>|$-7fTOyx0Rz%?rJsUY1uV<|N_H7PlzrMJnVj z$?;PJYZ;tdVy;9wC7Y&f=JDZk?RCDv%f_=^bT=tJDpo()df0vj%3sArLhn-6T0u)jWIYw6Dx>zjPgSgEbm`YP(HA`*?16ERpEzVX`d=hfH9B~@_6V4CSZ z7UCOiukmyRUy*6SpM95&2s#u6V)M1n+jiYv=#{D zMaHM5VxGp9T(2xHKBHRF`97g276%U*oF&LOM^a^HVqoOt?Cf9&a{`yU^yt6W zFTMWLeK^6pK>iT65aodaJEh%Mtiz0tEh?KWCVc&eRi;WcxlJBVrEfF_fq4jS7Vs#? z*3lBg6^>txpHyoFYY}8UE$$z+`y|HB(AU{K*JJ8)p?G3c>kf6`otbki^X62igMFBs z?t28xf7SXZGjXMME=uyMUQf%i=ym4wAjoroeM}!39iA>+{9yFwP_dpab@|FRemy%) zTX1hH!i3u@5raTs{^pM8txapQVul)6DQr{(m%cn=9YoGbb(XIUg6?u~kk}@x{DLqZ&iOsN zrOyz~tQwFdd}a$^N$}2@$K>aRCF@5f$q(cI{1|`v5jb}cpAfl#$`MwU&cH_J*bgEA7cj`tIHA38 zuSGs87M8GNF*_Z>C#xveWw_bh=vGJW#i8_qQlIIKv2oCB?xTjE^>wO+4@E!SvRBJXrzSgusrHrgc>_~zo#dGz_EatRvzL}^^O1RynBJsZm714Un*)C; zge$V0tsN-xY{SjN3lB|!yFBoFoG=X`eCC%*kca{f;EX?TWK9Hx5DKy8KCBC1kan zmjOpHYRbhDWoW=Ou|D8S6>r1_`)>n~A%rMVu4dp)11cAqJHk^o;6R-;Y912Ge!paW z>?r}0Gpp;D7kJjojYVTD&qVzM#z=^N$vL3C$_>2Gdr#D!q{w?qZK5$#2e(JBw%0a< zw2gPJRQDoeQsE#lfC$!KhRs=PytPg2UX0TEk2|I6Pi~a7_Vzky{=jbx%EQ%Zw^Ka? zE3j9WaW7BOrqkmh3?r*LJ-^D{4jDtsEuUBt{c7*p7?_73QO2O@p^F>Tq2|C+K4Dld zpc6Glf;8z4tvb8h1%Kp4w)%x?_Sg&G#X7FUiAP8HGv8W1|5^4fofZMMvo?~e{j*3R znZ@M08~B~4I!S53Gib!N&jR>_Hs1jkU<5&R zVpjeo83X0wXQkDEiY!u6zHS_^k1V>a-b>uQ7BXgSem9IGIWxfWaKIiGJNq{UxTQKa zIz;NRib@i-OSNuJFwsgiwOQa-QGNvT5CaX4w(iGS)SM?wQ1rW?G>>-UXOn56GS+1N z9q+M?Mujvy$&!zL9pSywn|34Q&wgsn41cA}{LQaq&Gw*ejQ(t@{)A8FcFe`%%el4C zO&QGzA`L!{bktu~pyYTJE);}AL zQy!hx8=9QO_*;H~*SfCwSuq+~IlZ0Bdw@ywAHFF=3tN~2+&2YJUp}dN@|Wel_PR5a zX(}5Wa23?4|ffhE!Dc9^x?zP00xiO zT9^Hm2R}o49DSem0YnGn!RXI7-A0&_zGXaVgh&lx$9J65M&A}SXo|P5;MhE60-1E$AUCmu=dARuC zPZ{@*yAwY53`R#HQ1^|d-_i|TxT$ktRZMSJ5KK5o*T5ei;AgetMfX{mg!o^PXgmBPv4p8SJJNi>Wv5~sRoRJA#T zVnV>F^wl!D_}nsuni+FVnuz&b#HS1o zf?KMUH>00emx;R%{zx%DE$J@px7d<7{O+7v@m|_}7~4(p1ue~Pzo`zHSYJJNC^s78 zV0rLK@ShaGe?$5gt%B6_gJJgi{U6od&nDjon{?R3jh5d};o7r*M9^`y@Q$>(CCnWd zGNAC3mbDIkrO!0hcl;gIRhb+2-Q%1agqXrK8yKH$+$@mA&1WA|RHh?fU$bH9&GIyr ze2yZQmpOD>Uj4`RxwSV+r=5^NeF`QdWP2o9e`GDnjjM{I@qCk2yU6bE-|)uRylQk@ zbu6QO`S!*(hc++m%!~hk3{vU;nm{IR21zn21!< zXz*3`*2qtbA|K~+FPcL;5bKCM7#sohmoFL}ZV#+|(Xcr`ZYV*@M8^@lDXJG7fc9cNmSWv)t2{1jXz0`Xlm0L#d=@QQN2}T$qL=15UeOWBS#|v zBlw929328Deh?GTvR;*0xTq{|={3>h=SbTjpyr%&FIhZMC?unE{DVU+%=L-tj_-ig zF3*eNGM!s^RQMm0E>z5aQc=Fxo^d)KZ~;t%ed6E~0_lK082!0I=ctL z+&KCS*E_x>&LIq@H|4aP2CXdpN5JB6X~HHH(&ph}O`2oIx-CM;cUwtYPcP;gZfG90 z`mfNwRsk>%0VHcsxrc{`Yk$juKZ*#-tkCqQlqTs`32lO3i3n9nnslpfb(W566d7OxHZTr|gU@|Qg3|}1 zKL_cBh0t|^I|Hb1=KAQ)^M%o({-lw-8`K%Z^C9Yaob63$VK_)aGfCa(;kMEgn8GqJ zu52RxK8%r&9D{c4Y1K9^I%O#B4`ItW*cus`!JLmu9>9FIFHLE5S`UJhu8w7P+K3`126M3fV<-GRPz*l27z2cI(Z0GCwM6G~x zkoAOhL>>%|w%lJfN|3&%VV%m|@u;&U=hJpDJ8F6r0ZY9uZV~p!_ioo{6(EBj4ipI& zf68eonokX484g#^+Dv=A@43uP`i-#DrIe5SouCq!hhQFn3^FJ1F#WhQGA|p~3GXh! zS=uEk^8E2Zr#H>B<3_#JTXM6iRC^L`6#4t~q{>gEUEI_i8h{0=k>&p8;2RyhI{s_o zvE+P@eb6FTz2K%3k5zS-cH@6(+-AGb`zFEo7|AQ7{L?e0xJJo zK`mSMZw}!7zzOZBKn?-(F~zr__^ju$T5{H|&zfnVbRY7RQdCygvz*rfwClksi3$%e zK0m83N#YYXDXN$eC@j?~5OsFveALaM5A8h6L+aogk|=O>ffxXzKZi*;=c=&W#(l@@ zt+AVT{i9B`IFClTt*o8lBQ~CBP8C1$mgAx8x)?sNx$(-bbydADx&6j(n9a_v`1B{O zHXOn?evkhNlLO4u*-BvlVHMyK?7R~?Hv`3gXI|}jME2NLY@x{F*IeoR+?6P1CBk17 zQKp&!d@_0-^YP~PqwJCGXU^XJmeZ}Ltn<*te#m?FJ!lDMX`}*o1RqjAh6!TeuV7Mn zO`tI&AgWCi5O8`m_?sTHHgwH36IN)ry5Z`m|(L@wxgtx1o5->M4;}WhxiLU!U6hl2skh-JXB1Esj+36t}-P=;m-#o$UMj z+ZM*S1&bP(NetBW7u+(Q^NPO^szA!Tg0B!gkSa6$)4U15&TXzhOuABo(w_ zWJr~PI=h3^pyR5T?8s^tZb%YJ8~2KbGt+S313t{ zJn_fBQLD?ATXw8!}Jj!c#6`lo3Slc30eZxix?n^qG(8ySc=BdTRA&f>V z)%b+1ie{Ma=G94jZc#}~11HDhdeZOsJFyKOmS+dA8 z#w8ml?g2aIy_n8hg)y=uZ>rnSXi(PTN(=JV%U#poBlYj46Q4+guglnR*}*#~AN|$h zqj~pA!UT9|vsrz<>LszRTvb8Z=o2@%K6tLwAo5c9p-k)43Kg7MTNjU+75>4Wp@nfd z>)Hq7PSwZdi9Ji3^Te589)gB6wsbHDb^H(W{!V`A2FmsjGM0+Bo8V}?89p;?`O<?2?hygJAa}BBTJ(l$TJNhi^>D{jPT-mZ@HDm^FSo1sFnQ(ydM8U><^TVs2~+irAVPSVJcXFyMeE?1VYC zL3f%L<1j7{F!8czdGzImcDG&-(H5~Rm%X_h`_)3Ofkc~LkSHP3|2K8n3j=Cue9;Gk zbR?T*qPx$d&pxplEre%i9g#l(>VSj40_yP({A@k5xuR|Nel63cOvX8BEaCSGdXv&rP ziFv8hHOBLdZVPWp$A4>H61D-0!zJmzRU!rR5E8YaBBqcK!SCKDt^}9#w!S1CyRg~u zy`GcMc64%dlVdI=OVrKf2Asx?Z?XD4=f@h?QE~%%pWj`hxjZ&As!6YrvEqGZnXg#p zY1%{RQi%#t20str0k5b3?u?yd8i`KKJlCZERn+|&j^cw;yuBO zQnrkr72atSA7<3$QiYN1sr8rG2b>cWW=dp%cHEi&mydnu4p{-CKbNfc(K5fV_0FNg z*wr$-9pf+t@6#20E^vA&N`HFD-Ix8yJi+(=aNb{+A;Z|?iM8x+yr?U(jQvYo)fu)S zul-{O+cW?V$MHm%g{`AGT#p0M0?peIi=Txti8*vGzxJfqt-147l<<3SLieJ^X9BeI zel^eCpWnZu6@ugQgRkkkP*av9ffeQJoJ@k+-j-!;{?`dKQj(gSWQ9DwzgR{mxq9y! zJU$<~gA$&hP(#*iH{zA(Xf8RjS`Wn&(cld1!vu*p#KQqhM}GSM#>l^T)Rt3D2`JsQ z=f!9cQyBinB)E=~o#2;2Wiv3${^)6Spe`T*--Z>#=Pscj(Xk9^Sn*_nX2m!g^}k3^ z^vS)+a9?ck4;6xW2;0!uVEDp`_#SrfnJkqmBTlo&XA4k-%#9)7I# z9*eY6CGyq7nP`(-=d}Kbi?KFE)j?11B8lg95l~j#(AM?)%=UN%N{~c_D1)E-+Yri% z{=4%|vv-n>eJ(NhMM+>RI~Nv(V3kz7E&m8F#b?$clrkdrLwIbFlFmlu)#IQ)y^Z^M z7z^ zKJ&;XrCOANA^)q2Yr8iuH3`cI6Y719Wj4XQnx^2i3!Nk#NxK+jE0jY|`~@JR0DYhK z5k$~++Bq=3!Szj5jgmBV<#KipuNYGn}6Z;SZ`kYcmuir3~7@eRnBa8+;+#sQq2Lj zH+Arh1}A+0&;fNY`g460_|dU($h+dj)kvyU6NX*XOG>&u-F%(n1X==SMpqcMz~b;N z*3$wib%YKTu2@oZZZ3cO#M;d&g@5lJI;~mj8pcP|NiYw=>tz9hUTz&41K_+lX~;g~ z6F(tWljf{pYM1(~epj_t&d06o^=Ibi`L~`Kjj2dypHQ z({fl?9D?*jiLA0&NgXSs=jThDY^kA(c9je44bSR=Ge2uMDFt8shtjrm1Z{5&LFGJ0 z7(5aUe3(2Z_U42zsPi$HLOx zyEBubgV%q0zwPLb4<6E*_@RAqd`(vziV5Q&FmM3Y0S{pG=Mts*!I;NAuzE&eGk12( zudTyQuem6ku_9_^X=u<`T@$Wvm7NdKZDcX@qIUM zdsqT^__^QvQpOl&ZENFUb=-XdRc_m@run+Uk6q4Uj%%rVncMk_FIkM*ngV4m;70{j z$bi>UZLFB z7ZM(z0r$~$liNuYmAVeo3P&tQ`tdD|*BS#3$w}T9PKVU!AagI^Q-Z!Tj!qmLTt~l9 zY7xYG;e%06`+Lm#+~DMR`k;4f(ev(`IyatJiPU18ApaE5d9 z1A)6XchFml@UCc#dL+*qrZc_UVafUn2@pIB~eH&Eyje^{^$ee+Bo{M58Jg< zK{+*7pg0}EMGd@=vbMDX#oK||2t@=yD(?w>SwRbNs7_0?o^;_^<+19)jJ~Mvrb8KD z+icGcEcfGZeBPDE!(uK?@-pxe)oRx+zi&gl7s8e|e=2+V7bT0!1X0xOO&Ch#2`Xb~&<$ zt!}vi$h`gk>wB+*vIst(cy*YFO8ukVxv8-J>JCYoaM{+koNAk(Og-foO!$3I=VN3TQQh+ z2o-7UwrI`>GM$;lcfvju32)g#flxX3;RwN&Z3FEKJElj$#bO%HM8Tpbw+K- zea3tZ74qjY6ka+Zy3 zUih>|SD1=~hvmnwrQGbMF9_{>DB2V;t1nN0BC;#uhwzffKUirbl6AMr{c^lN=FMx~lmea44reVM18s1IgN_QA$JWb0zf$URX7 z(8I^k3ADunEdhCseoL&zmniAtu(ISse@b6kX8hYz_a~ACf84{#`0{o}LkEb({!Vk- z9pYd}r}=(rq>BC1>R0+R1oMoWE^VFPT@s>ZvlE=aZpS-%?Olz8)J9N!>l}z zj}C01qf61tjaYsvqoR!fzRH{~c0XF-7-p(SIkxt7-nj&nk7ty$!d1_yC_rK;%GDhUWTyQ`-NRj zm?T&puA(}Nl?*F$Ul2Ly?6D4Oe`6-uEVJIbXjrtZMYd`eP!7H2Lhw!6dH}%%{s2go zgZm^1o}4f{Hc(x}tZa#oMR1a7uf};X>342&lIH5^>}sDB{CM?-wH+kt>crNZCj*jU zoS_5`dkl@KbI&b$KT}9|THPZjy@>|+z&xZ5z7NzB!~humxhA&}yZdU_W5E`8)qh*2 z$oHebOQavypI1F(vaeA6I%#zQEDnbWPnDutJJk6ulP*Q+8J1^XBz62M8IdugN@a&o zuv9!~z&r#^Zf6Txme^VwxF0xip@i!bK6eNv@?*`fSFUkF9*KE*H*7~s^P#4)O3kIH zW@3rKvP%gC&pJ?Ko!rlw)7W9-qAtH~R*UWR_7`^8{NDXhkeUk@5-jXv`p^jct>Dku zIH8L1e-4%9%;InR*L0(XCVtuln9(AwzUwSz6UKwdSU= zc$+)XD4}Z4^Xj~Q=c&+VUaFm`Hpp>|8o)fa87yso>oQSxZtiTT5z%I8x}>3Btf1_v z1`X)&N8eu^e4R8|=-2sXk{|TV#BKpSRy$>HOrs{x)~tVte<*G-Ina64of0uwh=7}T zNjcl$_j|Jw=DMLEyYp4_efv4agnz<$AX6M5uj{X5>c9P~4`s?!M}qpis<+lzn2_H& zc@25A8?BvQiQ~NaKS~VP>Gs;eywxN1Ze+sAw~r5~oOIIgvd`|@QEzTl6lgHEydGrlzeFGMTd-a$9d#gg&)iqoBAz`4rP0leK+Ykb|7GuULhk<5aJ-SE$vwaU z5(*#;e(Zw-rwvAbzTpgwZp)-E*GK!jH$aN|t)vlOJ=;Vurm=zM^zM0jyqh>!9KPWa zU)H=SdmqxCl{G2hcjoPbKuoD>3=?F=4xuNNWtBuSU><@Gz{K3f5FG3fK>H5w6FQOt z6jTjv2lQ0lz~(W+3b!NUnIP2Oc<+m@F)tz@BU^0de6cXBx%|fYrS*iY5JvTZ?`6gD z6)($))EZw>(yv<8LpWd@5C@+SNC))6=+8m=QLvSgqiJzx5nCqVorXMPTz680d3I%E zSqTwGUjX_=us9qf9!_Z+0tu`4G-w7$dNwp`)aoHmddN($`KEijU#LU9j1V8$8iP(? zCg5ee5r~}}h!Sv1o-lM0YOT!BhW5Z#`M`xK`i-i5^||2Qr%)`mEEPKU|-d z;b5q;mYbFM{5&l;jDaP->Wr28{JUpwT3R#l6cmETMdaWBF5qv6nD{HWNC?G+mf31p z(%J5otIi7=#rrrs^x^-eIogz(7M^h3DzUfQlP)KomEGvVfF?hks-$TcVMsLnOP zQd_t#_2Wsul4asI1@5X# zS}7=-w{``-5`8~k7)rbHhJ7WPp>lBziGp>t3TYHdX2kmkBj7(ANEyTn7(r5YVut?3 zrdkMc43rMkhQwS*qhlNp z2cP>;!Rdq1pKnMFXHUy-ovSazPY-aFZ`RN2M|mU_m*;;qZ&VID%Te{^5Tu;;oba9q z*5xgKr&!$OTNWrU7^5TJFkW_vm6+G@(FG9~#0_a`ZfyWxo(M2EE5-zgCn@$i8+4-mj_wjYRng$S=ArTDrA0^WBO+jnwYaU_^DOrrrMB zm+3X;L5Y;jO72x0s_R#_cZ+n;D_d}m_`rV7s6<+9WCs*)IeWL$@w}7LiZjfcdT%zU=2^0z-41OGFD2O%~{W&;a zt~C!U#`#n{zNmH$Y1$VpwxMnJ^t^NkQpu@XPYgq#%?<}=q=M$l=Q*|EsM9v?%hmQ$ zm+!n|%nXg*bA&K$_RT(@FUHpKuV2xQ-cbru>YLpZ@X~tGL}!E&X;)M)^I;Ipq23^5`FQX*?gM zW6@9+p|~&pKY2KS?0}8Gf~>mG@0HXoZ94hu_3vgM=skZ+9BqwR*YbUfQjWN1e9Y_+ zGMzGRWj2F>h*e8ms)XKIrHGt<-FsVKJgGZ89a7A9UBEm9cc-1Xos${N$rdU#427ma zUdjm-dXQ7Zh0Jz)|MauFsj^X$D3Z@7n4_e**7DOhysFv1%Fulj=gD?-<7;q!f2y5y zzIQ=QS3($V*+a2&Is(}`a}oS45n+-K8_1J`qLOsEXN;&9oDZyh@85sL9@HTjLMYp7TpANFuZ-$jkbG;QxGn(|jb z((I|-0MQ@UgF_`D1##@C7L1c4c|CrK%eWaeHn+7a8!x#?V>T@Lz0?JwLCLi>SsCD# z_W%v0kNc8dFH*c`ppvlsI+c>VWC5lu6+27aFI?=Cdi%@Hd_1X;tc$s+f#E;8Z*zMg zAzSBBuFZa$?9e5G4lawtezPnT7#Fh2Ts8bGH*g$?x%N+^EFAySt&@- z4|5Gzihs_~Bvsold4A|B|Ecqmdet#d%G%u286ln#-d5stG1H92!DXp1{nWI}_$|;s zrKj(yRNh!%(L{*9JZKDW<7D;EW~St#6Q0qTWUs^IT|Qx8I^RyH;}b)Btz&7&ztYx- zB)1VN1!5Tvw#SKW?zh#wGCwfceKpHw?P<&9y!3+o(p~`N=3|2asStAH8Yxge%F)dJ z=%3+$H5V`F9I@b7Ff=`UnX47&-A80yt$YToykLi&U-#yuoJ<|K?T*{*D&sm{a@^sN zT>Hu}GvTXUntSox-B{tivWc$}%X21`_VQ5V1N3$S&oPc$vQo^Kz2PTd&PYc~3g2mu z&Z2ZP!jv%H63gpu_239O`z=ty#=z;=dnjwO9%#YP(sF}ExMA6qbwVrOzLc+ovtEOa zldS&QokeL!GZ<)Kb^Nz5%944iSft_j>Fo<*e7UwBFXS`=QA-?&RpAQk7{6WPKuKp) zbE{*JkHCh=aQhcZ%JfbJ?t_*X*U88grCw6a3Ow zD|Af@i=>*DFyj=wOU{6K2v;_1D?>xu{m1+JSEduD-30U&JyQx(i@?9Kz@VR}K`P#w ze*b(hW;nXdNaF6zmkVPkVJN43^Veh2uEaYs++A<-xtmsdRnC!w9a%vzUqu_bh+rHL z2cHK=6$l$c0ggXcCk#f$UniMWHoSvA8F4Rnn`U^N;b}ZI`)e$5O6BoN<$JI=T%A0V{2t4vlqil!_hE#?1W|aHLe4LSQJXX{4-W`M07Rwa!C-OK9HG0*9e$=BXl; zY5B9xyPSG#5mMf;dUJYfcgs$?0(~zGQ+~)XiT_U?_8~iDX zVCM1jdNqu1Mz*i?h#7}&q&>2V28+XGiffr;7=JsC_Z|jS6tZ%3%Y7#rduMk^g7OCK zma)eTx4;o)zaM6jvI6P>p|ACKACZKvpE2B~w^ndpe<({|kaH1xSn(x6m7Uw}MF%%Z zpcIJa*q9&3n@L~91V^OZ?5u65x4sBewQG^J=^Yi48P?f|JD z)8i;_1TInBSEH%d`mcSONi!+vbct?^)hp#uW4K?jLs#{h@Rx>qqFXs16B23k?+QQd z%_VofJ_co}_RJT<&y#~__pnwAMXu=vk$wu6v~@K=U@SoCY?a@0l~Cpe%owS&v6N=| zO6I%%zi&Mj-46A~w!@dPg4uyI5evJc^W%Zqn|ZHG4&J<1^+_5(p!6o_l}tu|J6B+= zO?N&4#adMbSUiqq4lo#C%-k4sX*~K(Dw(JZ@glhxnH$=Ba{OwV`Tj-k&(7*%USDw; z=swfU;;(E7vdCeUmbM)xEoyLjJD2(1S@blWd=OHj0j;&FwXBWPA_4CChHKJKM{ z^KM*#?5zf!&Qe9WHKh?2|1XUTUwx(FDi!YB7B1~3Ka%#@?kG=%^IcY5P#0a#@W+*xT9eq*QP=B{NiS_w*?iDu z?=%b=1p67GyQ+bak*zaux*V4_`$q^%j+gIEjL&o#IWhIsD&3f#rFH4-?>ExP^yxKO z(G(1xYTc*R(Us!vNxD(Rfr(c!wYlbnLpyV;Rf?C!wJ~xmQBd!ZJb4Nyw!=){=?6Fr zr(#ZFo1D6-O+}vYCbKpFO%pd1lEFG+Vt+3m+{^!;0M>tF=3ksG>jG9!1#doGU8xoP z|491^s4AN-T)G=X1W7^ZmKFi&F6r>l-5t{1DIl!~qJRh@NJt|}hmrz<%2E~$psMWAF?632y!w6;{Q`-szpq*Avus5}HH?eT^@-jDbusALXaP}f+ zQDsO{enmnw6he+_?}HhH4Ad%nR(ZUvlVCsDi`>hYV88s_uU09^=^-4*E^~6ihHZXe zltdd+Jn~|JDeV4sv8CUFLIV?<^V_YoDWD z1W;Vv8H2{cB1qn*7-u=^Pz6RHpf$YX&La34w3^X`x?OU&{&&BqvrpkeEu1_hmnqcp znvK_+RHHv;ecVi2>^m4gOLX}}K<6js3Hc+S4od`_I7t64@4Y}vH9s6Z30>P)cVA4p z>P{(=>fWs?XK`lj@+c1d@vUQ^ezJGJ3X>vLnM7sAbBK6UpCW-4{rjiY(}?!5)JR$b zaPNa(j$m8yasWrh@p1&eC$JZvm>Xq@A%ekxTagV4_7{P)= z7~Mf929f05RrF?oTJ;(vt^4U!X@VR9-(PpK=El>RSrmQv!ETg=1?x8#m=U!2ur6TK zuvfCcYgMK z`PbX^X@%ZBRb`vKvnyjr3$NS3>kkjuP1U5DjMu+O`W6=nZYS&t@v{`f=bpr|5s78JGE$`X8^nP`#P?05g>YCfEO#GFyVa|V2IfyokckABTV*L0o*G@>3 zFpjC8!UQn@Mt`r@j zcV}Jqo-=sAS*{}dgiQDP*@uJc;p(4uA16FaqGJI(oCew3LZ$?$vY@@|AyPo(LA>S} zk%}mkZ}Y*S3~j`8l@?wl>eR4On0#1u{n>-csoNv;%t`@P-YnH{E zm~-1z1Z*_h$6!4xd@EbJkwbl8Mx%5fP0edY&9tYs(onsTCnAW+SpyZrfxAlDfmT?+ z#&NXhKx3IRYX2d3lBNgTaGW6a6;dpF3a^_A4+LR z{TedX)lVOeh()8jr%P8#FOI_9krpc-Wq0dkh^|$ngh~)cBR+KD#yF-9o&ik941m$! z!z94v^_u;BanC_2NhVXx!(}xD^P3)xLSKU#xl&k}BR+zcAHw8W^;^wbY0*2=z2xGu zguBkU0WMOsTkp3Chm(qxT5sh1=fZO`H+QjgbT#JTK5nZ9EW>9E?Tn~#LM`W;XCG;< zaf`T+CezcS{*bo9-XfI!wggt}Zm6?x!ye5+X6kcaOSgL6S80Asv`*9hm(7?E)A>F$ zeiVi5+1PLl`~mCOE%Z-dbttJ0ce5-^Q`yO@INTky9Z(U_}Cecb_sKdo8qS#4D$sdV~<~Hou`nu0qc}LG&(-_{;^g%Nu+U9#VaMVYv^c&QMlAMB6q31Tn?5e{A2V} z+5#S)sGMFA8$u&rS%ry<;;GEHdN|mI*_$pKsf1k^GAbSj*JuGO!7V=<5T4-R2|H@W z13Z#EXN;W+HE%#R&1&OCv$Nt_G_JZSD^SFK18Zw7(?UF3%8Rjvg!3-hEjZXmjV70i+I>pBqXo8XDKa8@DVo{W3vqxp6nf9j06~mk$a8qQvAm zP!JbS7R$)fimtTN+aP~n<&;a?^bqq9_GloqC?HiF-n)hZcSNjiZa84Do ze6Cu%7ktYbU97}~AIq492ybqM#ER|8suj}tY@DD%{CFK0@#Wp(@Ea>5bb^+*p?wPV zkTiIJmK{(HAnIWB_W*5og|3qFIGv}ENnBFhkIJ-r(q++;BE#L@-%w7VA+ifzeke~b zhw3(QhmczKp83MW{0qosjpsNQ<0>aFsO=G$l`H@~3LYRRk^ea998zG;rfUXaK0WvJ z7c4Pr?Z!8n;+0SNl+_n0!6 zBcIKm-4k$m#_0UeS*3kEQry65$?n>UWJYgM0%pam5@jPn)C061m&HE5Tq|7YcIK!m zP&of?$3A=~(5VBHQRR9!p-b1k_?KU)kPu-UQwPtdFdav=oZ(jRU+#1~Qo5t79_nIN z51EkXXnzG<5^v)$4m5k6vDeSFy87h9F--iMtnB6@tB9ZIH{(%I7wY!dA~s8uTddWs zce`KqmVwweB+C$SU$O9DNlSCbQ?p_5*PWQ9cNTjL{T_Mhm$j}u@Ca0YIp{9jyji!_ zS9;$(Qp(X4W^!sL6Phl|Qplaw;ps@V?{F7f#wNpV7p>9hvbQkP_t!B+l5z#kxl@B* z=;vqNbcNraU&@|eC?Bc*mLx!+S%hqm>d(J%+dtaYPsYLB4rJ$?8Y5!@M^LqWNWNM{ z(_7|$tMR%}jfmXgw%K0)%;86-Xvc_>r=y((2xPK!gMs?8CzePu3AFUEk2dG1N%xX; zS+N}5E;kWeKzOSmL@;YHI!G@Xego!lI{T(m;^0%{hhny}dzk%{95Z1~HIK=uUQZGa zDTF`ZL#CAWb_cydPji4CO3#tA$BTbqf4h{JMG)U?PfJ|-{#VPDxe|}c=DF@6d62#d z{_%otStlGlobP2B(6r|6+n}SJk*j@`$g;VknyQo0f;Ask%Ew+2tl(&7?Es9j4(4z^ zQ!h!8o1&CKcY>PfL>+fGBux#qvg3D>xZN&RzS5S%QVf=LgMq{cPwP{N8|~)~c2LlT zUppHT;*AJ@$-)n?%iOm>PG=SM`#x-<7AorrBIQ64hEv~RINs~-H?L`f@2K^TOX5U{ zh&c!?+^oe; z8XjFh5*1GOxwY@iz<~GE@+3>m&heZYT^Gl4)2=7odfV;gYp~oE8SB3Gi0ZFd558!Y z0?~!L^>{jZnm9Th*BkkQ z@9VDzFkw-|-0vQnAvjTRZA%jOUb2a5x=S*I%X2SW2ph10enk9q-9q%i=M_O2RkjdVD$I!G)Eo~h-T|MOTSROqoJ{RTkD=JTFJ$(0_9!%Dz>*5Aa(c<9@e?kDHFM< zruW!_ksORju` z4wplq5_w9@%Xy8<3nN*lno}ojv^t2nW9RqD5Cobw0?9HVVZ!(I?j9E40__iN@N@(bL>`R(9-{8fnm}+ZZ4ZBhV)b0y31gI_z}}-Z zuhFdqF&t@N>cs3hBb36CL)$mjGuiOa*JxzVjy91yPG}x~(Xp7dCK$ z=h2IPp2h8nvcYCJeA1wXui;dhSD`TjxV#y zZ{k^=UcQyOViLlnwwANjA1*z&xv+~_io=y3&p9*4vk%Y=YV2^cn>0yZqj}<-tk?`0BjAh- z_U?8dgU%h)Dur+YmTiz9b;hGE+3{Ol5E95bi=_4OW`h3fF{{m}BmI;9VdPFVn>zh8oJBabvP zM@j97@@E)v`11y?GbXZ|ge#PjDkVKZ^7wq>u4y#F{umG_G^^-de3cCAGm1Mc_8Z%F z8P-``{9={*!SjF#IF$fIZMnMfz}5XZqs2JHS=lQ-tDzxZYMAtO*pCKdfjXK2Vd*8q zx;SCi^M2(Xi|u%*Z0NBME!7dGB2E3!oLN^0oH~gfyIy9)mwnO=;e>uf{0Jm)kpLI> ze>1I1B<%%#`gReahd`!JQt=s{2h+#$C=<5m;5@aqrq@s?;~#l4rmhrkVjHkjwgfF!Y!9Pq4(nT;wRltr1%l5NaX#6{hPu|n z&7Q9=1nMo>q>vl5yBHyGb>;L-Q*^G=rB&CI_+-NTJv|wkWqf~O-S+lv`t(=7tO0lwLv!eFTuxI^jfuqMDQ5a<5 zERS!-uw1flaobidcm`v?D%Ep2wyu+Tf4Zy@)JK9}bZ(Z`E_Q6@_V!1Wejo+rj2FXj zA6^?eDwz*^t>{{H8tdX?VGqp(H#;_q2}(pI6~4x`GbLat&SY9u6gFco@-1;P6gEB! zVB9l^{Kz=t`s}g^gcH^&eP{$0-J!|qB;n!ju7>Xgso0Cur~Fe<8BEEkGhYz|buDbc z26s4mk`!&9Mr#~H)$hpM&`Cr=FtS{*C3;1#oqJBTcjB_~SBc?-jf=%)P2lH(Gg(=K zG-MMO=s)X2HR_M#pHV@GWF9hO6JJkXGVETM^=GU!3Cg^$FTR=9#ACf}(}zuej-D(| z^mlh#zi>any4Nxf;@TbYPuVEzw?=56^UDq`LS+eX;5j&f>(FinMt?6^m^}Pj`;mzm zS~XD$zaq0?77V7HJA;bYCdhLmn`;GX$8ce#G_5DxXTs&H!=d;koPErE%;52eiSJNXS=>JE|bs*E5CE+W@xJ3+lXye)Lyz}P6n>3syrby52?^-XS z<}Xsrs+hmbIp>wU^`zigoTq7ssfjXXh7mY1UQ3}UlR?G?6o@kT`$w7%iG$JK!_$`- z&67!$wY_NHfZ1m1(@Mkeg|J8e3jHU7eobp>>G$B}ho>E}9uw|1wrS2B6P5aT3B2+} zxy9|TU5_1fs5KU{hHK~m52uC6?99wT7%k}6;{;0Y0`4Kx8i=?4k2LQ2XGtIWVy+pW zT&x#TBQ6V-T-kl9V5K5@$*LCH|0k19JZC0HV(@Kpfye2;wK0eJ-?;Kn-^DJvEaWio z(BQ4xTz%Y!u}9Nm86op8xcOaQTWU@!TtG`*kF;)zGI{buvkvl<;=uE93L3=95ojlV z_Xq!jpZ{W`xqtiR)7sm^w2xVBRDN6?FZE`3~=yWq!z)j&Yv5W1-r;x$vh zYr;VZk3-EO@NYHgxjxY~ymEWBQke*yEiekqgOe;%Pb9W5eU)ggS-zY5M}Te!1Kpa%>8ek z2kE(MOi|nq$qjtkhf$ctANC9mkW}MN^frt(cgUsi&;U}0(vypjqchj~IC3?QJ6H~^!+mm?$e96vgjc3B5s@-W3tAK776)bn`XS4fcLI*LCRV4k$| z+kQu=L$!I+x%XnmxVJLObDImL@gos3U3jl*X4tF9fuaQ;nqUEQadEe`;^N^xjS4!e z4i%x?#1UL5-7k*OMc3;4EPqWaN-Z#gh>SLId5ru6P20n7{Ydgd$zQfRBdUCb?P&*o zwho=c8)vTD>|CMgy&sEMhMGJ*) zIoh4_eCeG9<*>*=n%!|5JGnR85dn#xX9Ry3TSBfZ^dsV@AVKuO=MeS%7OPt9m(IftWi`Ra|qu+SXHwq z&mcpUM`pQp7n~vR66FImDsl9*hglzIV}ZqY#;Z$&6xvf{IlN8m&hMfMgtEK$I>%FR zTBKCCDUF{$8K^HIeB|Qp`00y$4KwLdg0&gfoj`?lhN&BMb+2ODJLsQ8Lx&wU90NyS z{aJqhc73s$JCE{(pUtH_8K+eIiQbryqeOoHXcVVcRp}SQg7lVSuznH*7n8|s^Grm| z6{EX972-F2(a+ux5`Z7PWFO(IDG1Cgej(ybz!RYc%*<})AZx-N#4{C3u{hN)_v=sHPWI+NwuEcj~TK=eH=hjKF=-{kB7xkMnMGjXj&#e&BZ^$(8%=pmL0woSiwS6zk?_eCn;B znjk?PY5y{d?Q=7$4J{be@sf&@A5s~EhO6#vs~3g$!==nXWtUSQK<>Ei^@6tH`Rx?E zy{>YLPmOX#kIO4imE4qPJcv{c5~NLFrnc^=j~=sb%ivW(`8&L6pnMr}Wyv`JG409^jTcY*d;BePk1xm zr<}H1_LQL#?IrIQ^-lIrrr+6qAY|ZkYF?#}`4ydp377zNbOCAb@CP7})A^6JbV>^I z7G}ji_HdosS~J9-xJkh~mBg_ljljY2QPno@waqa=0&1gg;yyR)aNt%2X7T63Sn{4! zC=zZ<d7DxjhxHsI`$`Ra{=>_5!eoHg+BLpkb`{WQ-p(A8a~ zCbE(xc)3{xt)*_($CzrHw9*r)O$i(eaQ8;gw7}Zc+7TL~zmmkU(?e}M$k+pi2-Ciuu=NybQxc24Jf16=;*O*<@8m5#7 zRxbFUc5jcvp!Pq;tuxk)37E5?< zd0FJfH33}!$!pvQC%1v#B`WYkv|4U$@1Lp6Nmn+n>9rw;?=-{!U{Mt9n4JKO%4m593D1jEx8}A`2O+p-^$9*rbvD(cNc~?S<>F^+CL~} zp5K3cZsmd0UcR6b0h5J;y_M8-{1kH{$#kS`yFSGryYrF1R2%UCVr}UxG@p<=; zh4&@y^_n2>f2tkkf6Z*kg4n=BVrXR9mA@76;GDGh?FHyYe<(+64$HS#x*kjF?ZnIN zW-jumTDyL@MBp$O*S+#nWP2ABPKU2j=wt%(2JsOfa`QN^6*O}L7wr>s5^t`g?Zz$+ zc_tN=Y&e}1CX=zF0k<;uyUTX5%kAhb*LC7HW<>{svJ4yFyK2|?y%P3*)n{?8MfT#m zp87~Hai8Ir+7UWpGbJ^N`oZ^RRbF5)62q9)<2@JD+j2CXp)L<(Bi;MqsVi>I3eKlSL?ewo3x9KLoD4z-%Mg z9V<%kpvQQ^BUp`Wi?i&F7}dRsR@Q3nAW23r4^GC+tj$0k4h*EZLL2*G%FtO;C&EkC zR>xM6@!wpxm$X8@@OzfyIm1A6SINuSdvOaGxfZq;s&|XNFzD(d88}^d^i2}2f}T~4 z6LpxWiSH9Nf*|&vSlbi&fUiF?2J)uLARiU&M-zaHeI_R}1@&q1t&*ijhNaCCqg}@k=v8*vb;6$H>ywo>|#WTa!9EX$_bMXX)|*x;g~X zpVRM*R-C{`Q@3Wdl5%^s?BggJ1yb%k9(nxe>mx;$j_P;m)-#GWjozs&?&i|O?xXK4 zl%Jc{#@QuezO;#sEl-KT?qy`0|d<4%CaWMLatz341rkZnv6shkkrgL6zhO3z* zhSerLBGRD%i zYU+!xLqn^?@pN0dgzzqdd7zw!tyoyFyrs3Hlar&JyXomI4y~uTr{S6h^-#+6og{=D z>g!xNR-&lp1L{Shgg$xKRf8p=3)#udYO8OkJ-hDq{O+pJ8oF2#y^i{eMODeiH@^te zeo)Nk5DJ!av$iyM;S%J(dh(g4UG;8)H8vG*E_C)HJ{TRmjx~%^F?6{pwRY`&Hp%OA z!P1TnW{z-|K8<&)1yPAlpI-4r62vadwa*x1?fi`>9weyMFnJ-j2JrK*GKL)^^?c8056?Kt!i4O#p}4xbVNtSc z*P39(fdlI{8O;_JkaHxb5b+tYh)@}*ER(h6>3zCVV7aj`U>Ch%R~8r9lX{s=ZPP$# zW<1077x~&4-zz;u5FQ5_)Pgo!4%Vl&EixiMRVAnKN!O|?Y=4vt>O?3`M{OHlOM~ctTJz%g=xnvzRC4@*R z5RPYmTFQLObESVzg6?VS7@mLE!{*&x8KU*Z<+b21m?JNY>t znwUL#L(!TV`DJjOF($^_^A`sa;98)<$wwI5`i%kxNexvp z-Vn7~1|=idX|g1_?NtqQb!4`*-NKf)gg(K|#m9F_MktTzKH7z-YqG$ zl#*(w4X#gVT`p5xX$zKd0%ho6aJ|oVahfWqGiSEGNKx++&ux}o7jWJ({Vu^WxuRJa zq%3(OSjh?0m$5TvGk359mYWmqes~n^&CsnqX&%w|K15$XI?qlNZg>873i9;xZrO}U zkm$_|I^~(4T6&_yao=G5psQ=aQgx@wP7~Z^+H_$WG2)TeaAF)as!j(f=)j-?$}zhN z@SIR-uF-Do;G!P%(2%XS_<(626T+L#vJ#q*lJruQ(C#auk`2tn3T6odW)%Sefs>En zs}INuKg5$$en7KPy?dkg`WsS1Mj<3I=U)Ezv(!4h$FljDfG0b4JNZ2}PTykbDi3Qh znSy^X$;x!etkDj0cnmGrNluf>9Zi67-5eOsEL|MEL9u@5X!v8_J7c&_%ugZY?pw?= z*OM-{Gj8F|xE8m^>{wm!y>;F8(lkfQ$d*;ueM4cL)O5qTmuz?azS8MD z5N1l?&Vp==`2QCZ;D13|-w_{R^!HoeY_v=Ywpq_xM0}c|7k6IbCgR=}LzXPLg5~{6 z&?>&+L{W<-E)djN8w(O|5xH#qb}CaiSeNjU;EQ=HGFqgq-?+~J6L6=53uq<_PBWOx zarpe%9U|dKYKcq~tj>On&m?eHBc4kwTq0KfTOdhlS7Y5t5Xd*AK*m}4OS)=3q8%T2!$yjzhrg&cW zf{FDqmO9CWqZY;X+BL#GxP)o=#ID}))bXc;JVzME)WP$g!xTzq|F+f!cvr~g=3iNM#dhXeLMG@uwq{$O*Z<* zw>5?t@=IH(yxRhj80KkOU>;nkshPP4*r!f6sWa-Y!8{*gh%&WfFHi7>6#oiiD;BFo z{(L>HIu5VOONqxHuMIbHV7Fwum%^3qkgNp;==X2>Ln_J{Tn zM1S_Ce_L=c1m{uexuhp-&8@%YKCe>d;?NS1e4|gju;Uz$U=Vgv!e&y#CHMx42rCV>$p!a0|}K$<^G+#r-%S5LhnH=q(EEXYcR5W$Lt; zh00ZS;oS}Kuk0d{p2R8NRcW%*tpnr6GV%7VIQZJti9YW&{KN+h+JV*$i`K0m$g=X zDp`B|@yC-}^-VTO(~U9rJ~=uM$w&RY<78~!NP+s*pD2-|P!gX z_sscW8x2BO<4Z-_x7by03O!KK__Z!Swx_x{JD!ks5Y+GU%sysCE1H=uv0kSAF`p5jRTwTm**sUddHC{u?MSj|Bqw_b#Y8NqR z+P?K{zNx;p0!SUoP&bz%s&2y6^dc`?v+eXR6|R{{%2AuVRJ*{!)zN%q9WW104X}fG zg5rLr=2l0wd_gqQS?$T;6^b82SwAPg&?1?N-Lk#=P`>fE;ZpU`!nizQUVj(Ha#wMV zUB=a=gZk|G;^cEWoFnNIlQ+Al82gm$FR-k9gJMUpPU)XQ1+f4|f4|)Hi%ik88eArK zBsnO@Ub!rA8#L_4k(RjJS2PRR#f@qOFF%CJ!>V3zbc6n9iW{1EYrULwO$=_;1vK3{ zH2*q3q8~YBU>@9Z135-^j&O#DGrIbN*Dh8RCv(_DV-eb01SbV8tiNWJ?MxG&+N2%` zq?i0{{eYvJ@#{@HUyOVl^t{dAeJ&Au*KVXe{;t%@qCqrV0Y%57{hAQj-xa{D)k zsHy_e89n!$DUB2`?mV&;GJY#SnCcsV)k~kZqFEK%bqrBNS6~rYox!UZCAo_&(X9e? z)KX<51v~ieyvc|Q(INkrQfXrAW^G~ubn(Nj`%n1m8DR>q_+VW=`al@3MWIoWvF!Pq zt#EHM!UDsXOF=`6ST!9Z0a{~R$xP`7(wWo|T#D}2ZND1i;_8`ehaQDE92C7hG-937 zKZ5Fr1u*)1Y2pcbV#XG9m%aAli}7Y=qow_|>|NwJrc3w9ug?12?43P^%JbsRs^6v{ zriaJ*JmfO0T#=6`WP0@zIzEaA*ZXdLfFgC_qzUrmaB?200njgcws^K+6NU+5Qe3HQ zM`oT7Jv8Q|#xIGOq?0j^d()_IsEqr)#HV~q8KTF!a@FG67raN~GALTrd+PGU|Y~>ep=@Ef6KI-lsHod~u z^LS{_Ib3j!ExRAw5@#(n@7$AGNbJouClJoO%A+ZNIfT-xzhw)u&cN+Wu2ADYH$Kj* zr)S(*%h`vMd{D>@SKGlVxo2yR<9a(o%ph#$&Eq)fa+Ds+7G1X4b7xm*VTCIO^5*T^zbG25V(7 z5AK`;rGdN;gKG{A$B@DO4BY7i`}Yuf8ckg)NPdQ$`vCKJTir*5?L!0 zuBz`hZnqll$aKVLOVD;b+-2Zj@WTUS_^3 zZmvetHks9;&^_eLBTxCh7!9-h>sv8F*_F>W;N^$1G|_lNkyt%2q$auc(j>s5Co1dBR`w4_=SG1jgTE*8 z^N%4~cQu%)?9b_ltbe5vaEk|O?&Lug7?s)SPD|= zeZU9;)+zl{s2~===#*als8NI!#laIyti*j+7o?f+QjpfHp( zNB@OuPS|0HNl8Q*U&C5|DnS0xGH4zBc4$#c#W%-_eEw(nLum1RD9eOl@&k#selWos z9_y~n>D{@*oA&PSNvc92-3jZIJ~TSq0}fdLqraCe`-uY!#U9r&^=47MHb$IjF<27v zK*|BVR0T@W{z(2dc=@4h1*~j@V*Fwi5;o`V^q#PGO}T!tLu}<2hm5!L>tM<6kk$^j zje{JMKe@VxD4qGbQjKh^J$Dq zpz^aU@U;m{uRJW%l6Ob0&`Oej7>i)m1wsM!kTiHW0q7sEEBYx+2;as~>h|>NF>cLv zd>kDYuHI*H2~TV7|EUveLVXO-gXnhD$6~cC27?q~%J*619xN99ioC73Zs+opR#vD3 z(!t@PxIIBNZZ}tV`{SxpFvOLC>wsyJC7hdtInzuVu|YhKFM6Rx!I=1of-_ND^+WZko^S4(Uo_M)__2bXDyh z>ZM2}Bn5@A3J-&4L<(RYT+oZFC(IJKo}k_Q&;xkZ0+=D!JMhMgt5yp~I@)7DCCUEe z^UgZYFp$@3KTL-k>4lSDSjOIapMGw*RMM<}{^i7oKmQe(Ca2np1frFhbEDSKk%xXn z{0O8Y`e5|;%grZ>Tk`TudZ=%H_W8hy_}8uCFpZo z!}Ek}5{D2yAEx&7^s8GdL=R)}p+(>$^p!3n%4IJFKa)|2xDW7<%Gx@b zT3I-{ojU$X9Z+S{lV!+flJKcBsyWh=wI-auRctop;Bqjr)I4ztmN7F2f@gN>jeJDJ zRiCTJ6ht{oLHXI>MffFy$&2C@3eiq$I=0c8KXib*)5Y47`?RB|n@KMk9|pPCO?|Df z)v1P?vc2{1J(eFe_$_}jv81K<2$uA5hbx%x^(%z(i4XTf%4xo<4i3=*#eR)dD6P1}}@_VVE_r?!vg={yJ-*y7T_uqKJ9&(_2ahYrGI_#2v6NV0|E9>Klyh^m1>F(AZSI5w zsLt?fXX#VOhBiKefiD_)u=`k-?7obwxgmmXL9^aTszLJ$LQS36YSz{e4Zp=D%@1i* zcq)0=TRQDa(-DJ4l#s~_PFP{UW=)n;Q*=GmHDXnXe|9sFZv-qT;(m7rP$&}^RJnco83)Ar1Te( zKU=lLOhdaK#xeCHm=43l{;_D*;s;{EhEyJLLi(@aS-tbp?^QLv)lS_IHy>x)-b~S6 zI)G!OURBi36? zb!9R)R`#sj@1;Y!#8$No>95#u3><;=#~1N$Cas~jc)n@76MNz}ckONzMM)}Zzujal z_$S-IYw|FhKaIu$Vr1Q{5ztRWp4U-P`d; zid$oY&!cUrCtx1j{$y=u1+za6A_WDU&$#r07i&uEYLY9PQVJy$>}mrdu(>dBvp=_# zAz!PpbgAW|?Z0@-@B^pC26-d4A5BpJhe1Uo(xh{}>cPv$Ew2Q0yB#nIk)p-Nz$>@bkw2gBK_ZiU{ufLQzhUlT}_pPii18u2JrEh88 zu*a+M{ro02CE$x*xl`63-f0Qu!R=2Tj-Y^w{fS=-6qq)@FpImO0U#xk=c*r^S) zn#zc1DZ|)r9Ool1#A&QQkZu)2CrnJW8fBE|m*wf4oNq1C-zgr$G+CBM!g-+<)DD^2 zF^;K&XGrG&OAcZHjQ(Dds!#?uJbn{1seGK1;jBUMj9aw3GIuMs{ZqQ~uc-8c6D8kt z)ufX1{mOx~y!GyR)$nG_3e%i2-!X@RH|6U6P3n-X2JRMgT)*Kc+4ihO{jiBcTzGtO zc*bhboY2eS7D_%=o3XyMuUr?{iOb` z@wh@X_iMMeTDx7Q1{}p=7oY&gLzpOn{U+~_Ck?BQXQFbtWAvk(CW@!V<3sjv zgDW|RS=$W}zb+OYdc()(w7gqK`joU-IATzS#pmQ}5lRoIOjRzwFt~_qL0NX?zLG(l zj5%wVy*?jzhg0Q!SiHaCR?u_)xMwMM`fN5IZq+|k8ITd4NOzsMj2q2;C0xn| z=3;T0nx?fvfgEpt^IA0jW$Dc<{5M7p6BXAirOUa`^)u;LFsjQQ7j!@Q8vm;ry_^#a z^!>B95r_8>mW*4h<>L!NwfKS`na7R*5PTO(Oh7-f&%pTm6QO^Y2Sr!yn( z;7&vT=6I1{1|pB8<)T~j&4*PRxf^$4+z;ls`s$8aCO(bJt%oi3U>;mI10To33oxh; z1#C`dP5fAMeU|Q>=duf{^p2s-4ZCqopOdq#nqX6yl*CoE;r)kVscGuZ(e2+@KGj%i z@e;)o=1wa5W*7u0Wg^}tGde89iwp<@PM{s+h&CAg{SMNEKGpCA$MI|ZM!mx0QDMaU z)Av;nQ|Jh)6=GZt$hJ=cQ81SZ-!18h>;=hh=t}6)z49s(Bez0%8h?8o(~)u4D^UUM5L7U<{ElTEW2@ zr({_WZ{68L#nX!*3-ihGpN5(A(3-U_QPya$CC3#pc0;QA;yFEeC$KS-hlwamYg zL>`Is`a%BFJunaMu=WD6%Ev`2fyMreZp@(3*;apJ=Vw92b@sOM7DV5>v-2j|v4g^V zzU2E)u2rGg?+u3J^MroB^v&!_<0E{0V3j_o=h#>k5XZPiRrA?%E0D#%$4FYYo`u)d(cm%Wq@z-3NUR+6f)vg{5$r+q?0O}lI3F0dc`#V577s%2%qsABR zhq>OUML)hc*Ff}xXI*bugh%f()^f*2=z$AW-bhc4sQr%jD`G%+wbX`?iI( zF?y9j5FfJD9!ipk!bpPcB;6yfM^;otRH3e@ouO7iuxBMtUzBFalG%dGGGb4N_-_z{ zf5Y6#^W-GR?~ZLLks~Ge#G!b9Jw8h`tt`zBwUf+{cAIiH4kjHVZv)g5v%`Dq$!9!B zEt%u4DHgopdBNJerClcwqcp7b@ly>#vngHH9`b0gj02~e3n%BP#xQvMH;tdtfj~l8 z&>JPEzHMz|>LkslV`w+!sy-SomtTX*6@a!Jjt<74;+nOcv8ki|3AG(%3S)NuUrLV{_ zJ6l}DdqU!rMJMc-PxsJW(tkN2r&Q98)U4|77sp3Q>lCV%YmR^S>ha8rqqwH}&b2~y;lqn}4Ij%PECoEn zwn`)^_obYq1M}dPh`EacFfTe9TO8l6&t7LFoGiO&64RJdP6@g501vM_kM4$9cg|FK zjt5OgBqh#Xkuq%O>T=}=TuU6JmMNwA{7>IvANu=@Z8%c*B}-JxLrVbbls+^%+)cop z;~)Ll4M>f$=B1;WB|qMfqIeU%#Y*Vhwwho`pUr(6k6qz2cnlS#;8=!Wx9{Rx-Dv?~ z!76RFgac#4z)_fPJj#ZJlF}705AMKr0Ckz|k3-qcUhOuRB&5V1eQWq-s)GbeM{||$|D-6DnFX}|_1^>AvbazDi zLmM8Vf4p7SR@2(guid?oRM8VYoFJuilh1j`1;4cLIhj%>3#K*uF+`^N$l7yd-k+sq zP`A{TBA0lXVd`eN6nCR=c%H}FM%@CfqYe)HLxxIwfWCl^r}C=TetFbtyTcE~9<$0I z7T=|y;nIGJn6%@p*DpvR6Yws4+x(t~9ziG3wRS&(Njp^v z{}&$P;J8uZQmrLLDobN6y(pXfhlE25&3^R^Ue z>3rd&VCDj9)SH8zg{D>~A4(QuKxOGDf0=1162oNb_uQ_^g*g5NTL8|k9;dIH*sV}m z4{K91M;F`E?Evq0eV!RkoUA_mZ*i`2yU*>Bcbz)e#OT9RS&A+V_>U`qbR>`}<;4Y( z!B6Pmo4*&Ods12N%)F zTBT&M6N#(QbhwNakBdEzE6>R<$QrJKk24olKDCs%us|oMp!3y_7mI0iI?`U2`5Re3 zu8b?V4_cd@im+KlBb7(q6FG9L%hNgIzPhsulL(v(=uJ2uiYje>kbP8!e(|Fs3s>Rf zxXyT3{ldo!HpB*ZCtJB+nP;O$Kg_2dXRf&Q`p2E|U%TP5++4sO!_Rig8`^mCdw$XP zBA=#IKZ)l@O!@D(2(DvA2fGA_bXpLc?~;d(G)p^BKkXE>A8{?ki(Uaes^dN$wHL@g zz5HQ)+4d^2;I9i`-dSel&?@-2I@-Cpf`oLqyP?OBPQdU)$j;Y z95rddk}n!!9cC$gux zsTJ%`)yqRCF4r0B@`jkr!q6&&E5Gq+s*UeXP+&zw(ZO6E)J71ccw>jbDswBBa1wLy z-a2?NBZ&2xg&YviZA=&L+pk?md!wVL00mEg67+wFgGcDdIie3nf3KDGM4}%km4*6# z4wAfE1G`kN>qpsD@$?R6kMV4;=-tKZ;N^!}Su@4e_}{bqy>{8^zQx#ryNNgD9zPOz zTpUO&(RKw>2J)sb2OSrA_M!&6inX)5xdrIe;%aVg=HX~eB)#tYKj+Ab?#q+H^56-=+9_PKgzos8%{NeUkpq-E)_5qj&=XL=d zf-J2~9pNtDXRYoZ2GWUMxy@b`?|bpRFiqOVjf7W+2(v}k2WuVi*8h6<&huQ?@x1RI&vWi`uXEqap|=yBFu?++n4<@C1}|x`nx}x5!u4=-G>TKsg75~bTl{kt4gE5yi>}dgRX^{rnLo#EEpba6JQ3P z)qb*iz%h-j?w6h5m0y#)UPXECkpuE*tQwK;H;6^Iw zghO!-?sQw!h=7ccIKk54;?{wkiWe*Pht1=bW>afA4a2r-0lo@}2gLUDm6% znWIejnI&M}Dc=sTJB;8d>hS!OGy8T%liMCvGH3{gtNal>ry~%{&CyZ5pj}4-9Wa3kCk-ABRutqp`8VGV zz6!No(6=dr<(8Bnl6}2bpDEpHHvRb0T36Il?tVk%I|4}kyeWHGo5q7wjFMsXmu|l} z2r~o2+gaNxYgfAL{9~%XyiFkgZ9U*(VLNjxLC@RuZ|=v~q&MPVK2!R5 z#lI&d58?Y@H%qOzLpEDTBsO0>){3#Gn)gul{T5Ec%Jp~zPNYTZTxsHb-r1`qnV#@~ z-Vyafm<|~LqrcyH+t<8z=5p%=K*M1CkHa&{56AQynbz$pi!fH@sdSd`k6@ZgDMdx3 zmRXQqvv#U+@IGBaLps9`6yhHO!|b8{B@#-jtZY zmm`+fAK)rSaUL*&c}yM*4kZ-C0T}%~P&4ttrY*a(*3wdjG;vOMz2ysPn|mp3$ZmhO zx9n$cKTb%a7w_h_6nfTTJVufAaF?R-~4cvLK zu-CDGfh6ODWd&Npo;8^6SKLWB${Vm)_Oy5>obSmxip83y5o)f8el{T+~igX*LQt}4m$hJ{|_b(K?70>;^Ut{tEAQss0kx| zCi>=*{)2nVy;YlDW z30KzsnMH}!mVd!^qjKxF+@Iff_?8hP&AkB+C>{cS{GRTT=DJoOS_#Tmop{?G-}a%K zH7~&TSPXF5WPW8nrt6esq_{~*V?Js@xBofUT>@DBR+`+b94B$E#|q=-NP= zJS~gQuAV;JPg`ms$nQTcA&0RlJ0rg9Fr?@W`D9Od?H%m2ZJ_OUkeGDFG!573Pxsr? zn_qHIluH>bGm|Hs%iPs#Fxqh9duYn~zA-(Z}VAmdG20 z>wp>@@=C8Ge%nD(gE$qbQhznG=^Js?pI7G~MGE^A17HY&b)ZIZg3;fDrT-P7cE>@% zZai~X>hrGEC^xD&4x1$%!cAQ2OX95ekM+*5k0m8n8F=1)5;u(eu2I{3Ue4Prc&}&# z&wAu{XdW3P8BPf(OEWDqkW>mSrvoO>c=<*{WT;?J)eop1=+di;UTEmIF7DCg}CO zeDzINt=~+3cgiSSDi-(>Etn=s-Pf=D*jn2AoHJ6C@Q!p}<#IR!q)4G368{g7{_W^< zZdhz@#z&~5aP*mvd*Y8HOE~8L(Ih8BF&g*Jd|Wnkvk1PT=#!`~t7#3sUE>LRA+D-5 ztQq#zEhXF~AvjyG;^S2%d+?zskIiUMz6D-o=kNzR5DKU2k3-3>#}`|w#$0h`LzfeG zEg$K>f2PSe(X9wArWsY&f7o-i7oNqQkrU5jQZoksfE4o#} z(kYe4_%|+mFdRYqKtoOXz0Me!YDf8Jo)%9cI3ZgpY3Z=dwwn7O9ciyg+LFOy!h4aRg;Jbxa8tOZ{+6 znonDMl=xKr)_K&-;L_mrq5KS9gkh%t+?;xv)^>WjpuWp-{y&gnXAE6?;SxbBAm zqT(OM0_Vbivnxb@vqhq}OD@3~GmO(QO6G5WG#1?dmhH`GpV11^x(VW2Pr`}y2%5en z`XzTgcz)+81`Z*EBA@>nF&OEsDJkJK#dSsa)nz;!@{#wu14iVEimq>-QZdOj|KrVR z`k?U&oxtxV*BUdv+zG4ok>AuZY|)Rn9Xgrbt8iLC_i#_y&!)OC(52Mc(g1#?1Q7OybWSMDT{&&nNVcp@OpjMt?8I;;-*j(Ka>tw>=A{SVBvBkDC|O zl-tDfDXjVTuJZ1O9`JJb7X9i=QtU*yQn40?-u$?-;I&wkcFncxvo-fxpNjXYyH5fh zBH%T^+{H9O(@azA69wXRHG*4o=^T+~Z!lH9BU_?qTPg}0?)eSh3m7JSb);CHpie4j z&3SyXs}o+*_sU@vBlPiU+trlf>qhBNR=GEvnVlTKB#^9OFAdt@>6_V@n^{=t8lU9i zBrrup!Kl%M9rXJ*eHtoniR#H*NmOL#;4hri$&bSCau>2-(K;rQSA?(r6Hza?kJ$6G z>(4}DstNCCgZ`j9Dj6%t5)p4m-NkGyEp2oRPJTm)fo0$^tawIu;q4+>QPf>SB@@*h zXWqxG(YLfl!$^1BMJ%mh_9rFZUdu$4KNYB;l9+>Ge8Jwl#6wGBZBw|8qJi0xm8$wm z)?ErX84NUmTi4R;q;U6Ze}DB?%3N&;>pe>jg$FO5Plh4ON;i*~t4WmEB$^FF z$r>mSW$^Prt$`B9TO2$8YFyZ{w zU;kbX(s*GF`Fr3ny=0KDK+vs?a%xTY3?#XhZ7RlbuDT=4+u)kbTd^|q9q_5JkI!mz z(C6L)W@cfj4{B5C>cim09uDloGoCX3o`U5o9>KU`ie)D6gD6$0?Q+VC+PJ<~!ALYl zj0658OBNTEP9PPbhnP}r%&-tr+kYNY^KlW%W_;jWV}FMUa&hDRPc9B&J7naaV9Vi~ z+m@g%c{!+<@IXW)nseJhg`t@q!FF!IA8DD0GVusDRMK-dguXX>r5g2x&Jt*k_wOtC?0=f{$iz>nwl zpL|(=La6RM6D-8}^iSy{Ql)^R)t%nxyFc73hl83-uBlb)&s}XV)g(p@eS*##wfJ7} z+R~ey5+1bg)pz{sJ?(ve2Szir@bg|QV+{{yMrl-sG(zlCY@9#_)b}H;@XYM|7lmfS zw{wRy;E|5FqkI(Ma%Aoog7-`(V#^|SyfcsWT`370Wo_0TmC^g}xf0*Uh zUyQBou~zqS({Ia+b^(9oX=}s65Okl`w*==jTzDNQ(PuoQz2oV{+ioGNg?3QAI^G=C) z=z83N{DS*Xog3M!gi zH6ypkM7wt^F4we@#b^!tq$Wk2&9q8NAhiz#qI?JsT#x$4j?uWPTse9H*^9SogiEw+ z>IrO{eI^Ytct;CGb%p29BadCS=0e};E#&z2?qTjUpR?W9XI=`4dyeA0m>H|yaKX`Y z6|i(l#A_Q^z%0$-*=9#sac8_Erfy5jja2q&vis%pykzxH{=9-%b~>H*g~Le?z7F`5 zJ-bbC4F?M)9ASk~=Vr<^^LuzWnWg?Pg7Z=iT1AiD^P!7$7)R6(VFJ1)q#1+J-=BmM zzx)Pur4wQj(phl#jlbM0LoJY-5lh9&rzHNay}<$f^Wh@iW(F&vet8++;pb%I`lPT) zGo7rDtC)yxtcAir>P3=-|J-s$24>)oG0e#9NLGQxbjJI!-nj3)GS%6J?2$`Zu)@-M zlC1+F+J9 zxW4Zh1S82%38Ee?U{AhjA^7lXDW$hwf-yb1J>*n6m33rh4)g|xLz#YdZ>{@h2M)}4 zulk*%SKN1I%dwta?$mx>ZoIl=ADMdnt@9PHP`q@}@prq~T*#p?Q8DG)$%PDJWWJ+> zaj#ro7r^46QxNTd_6bD)?4+IS6=;!o)=`cAOQGsKv9f+U*Mvyt6pE+)D|x(*dCf9F z3cj8O-&w7yM9Ql<2vxS!Sh?etmGt4CPZ93Vwg_@;>`$fzpXs-UfGPhADC@{d)A*!H zZ$eQ{OHgF|-2vC;qy&lIr))D`+H;D>umU4UIMt>j09Te ztvu-g}7|!>sM>>`pS1O1@sdPuKi_ zMwadkS4z7RXD_t@eSD;F7cGS*Ru$!~_u{r73B&}{KR#xQzG_~@z+!c-$FA)u`;J+k zm+UU;uh+H`95qgzBHv8+q;-v9W>zNJpbL|g#Ys|<${m$$G^8!e7fr-2HQFW-VFD85 z_%HdMQ}KmdXIy{Q<}RwE>0qsGbWF!Et;2nq$Clb*9LXy{5IOg!tI8^ybN0;;mp~&L z)U?v06>{kp9?#3|9Luc;36j^nxb^9*IC5Nd6+%oV3Y}qugz8}4sdG!$-0H9yEtFh% zR@3C0JnI!v;_a#*!(=95}+%YxMGy)J>C(7>K3QEa68-|wUG z$rzh5EaiS6Z)pC~iYc*$6e1LeGWdBYV<6&S^!GAGDD7JW>(2E1Xl-k{$HorTzP!lY zAJB}KQy)i*nhwfINbPc~hUEneb?p|5%3$6p zKZUlY?qL$zp_~D?CeA32y=GK9Bj0~|R2$sAn$N5G_AT}ckKB9A*K(R4yem#fQ2acN zdyAFD^9QbJK1yCe{gkXmlWsAr`>s@1bJeVly!9 zk3)-SpARHPOXMVcdL!KPUYI@XE4SZ|M%v>mE%)yJzL!5?pyAUvt>0Oo*{qPxonoA} z8Def+ndYifIs|w))v?#u+7x82F0MB!fM57zRRp$w4XB|uy9Uua0nMT z=Rs!sk<;)D)w%wQb{$0|yhJc#{d7B&x==0LTwmJt$4k9=-+tL^9%+|xhlT+u_{v(I zoy%b1Sc~sv#kp^V$rXekT_-~~&YUVtl4txKHy<*3Pr0WJ%}l^Sf7BNfoF8Yr-|T^4 zEI>rUg*}+|Zd<2pv@dzGca$Xw5Y``U?^_XQ+!<6LDJ`I- z$9O!$AbtnZ=iwk-{Va|9$hZu-`j{U?8?Uj z=ADZ4!>mBIB~Y-BO81@}rx&c6%2_@=A4y5PGA4rW{0z7qutyY~MD=Su>Vt18dz0ZZ z-D{;p2LZL3m~yp%j0eMR-n7&iD9Qys57W>2mYPAz5auy?Fo0wfSWo{9lrjm)8|mwp z3u%R2re_y^TyIWD%TR0&81li139tUugntAS1I1Sl-3P9SY}T7M)o}({U!dD?l4UJC zHbvok5TnTS1FUKCV_$%!rXKjCW2vd5XQ`*DsRRBx^r8UQ7-X~^8{x{`-#b_5Lac0f zqI~JXG6UA1mN759O_RzkzJG6Q>Gh;ic)~TYNey1%JxzCZDSt_%G}nLI}CT0Y_j2 zZOJpU^|Php&WVLzUE^nr8NVvIt>W2vai!*==nWYyhob%^Zwnidf8k$Pk= zy=K|*Q$|%NS%OC*Pcd)|8RR)+J%sGBsdSKhhvsTUQ6YT+#t3tpx3W_6bA~`G0~4QBU@!<3O^MXfLnssjIbCgdFlXz0i+Mkp6oGlC z1U>rJ2`1yXk_*{GRBYE4 z#wuFma_vXD617RFpOVRX7CAcF4~;vPNGP1TTjJMbO-f$G^K+ z<)>i~IWpxv-Cf2x$OgOC6kPJ>2hDw1;4449<*YzSqNCOkK)E?yT#0z+<(!pwU&_vFUkg!|c^*GX`o|D`o-19)zRjU=;RMmv$1;)wB$CQ%ES zb%K|}A*$Dq7%Squy_RQ@yvY0c#b-9>6mcXqs#*=J9B ziZ54BT#A1^)awW&n~ER7-Vf<82uWX;PPwd}&;HcYR_sa7n1iwP<8>C-D)><7#|nc1 z%tX@`+-5wv+r|pANRE%=64aKhS1+wZ-8!)eiA(ACDPtS3*i!F3QHo0$q3vA}QSx;& z!J!F}E)@HK9ouhAFwk8}TJ`b4&uJI!g#Hz?MnieL=03a2UbDkde&j?qZlj3HVWsgX z7zm=ew?4Zt_)!rsjev2)!y#DkvzGP$6E^tYc=<1?0hz;8I~E7V1_IqCv0w;eQsY1v zUyNUDA_gfUd3a#9=@DeYxl1>$k6&opgrhnQPa7gmlZmO1iFf~PRk;M)TS=6bP@Eo##Br0PPd|e$9KZNR#J{bM|=1YB%#Y5B+DDwMv zb?sZ^FzeyXNP@eJEe~R5UX5tUp^_hgB-L8@VA}&Nb()%khyh!dWJ?Uz_5?9AmCHoy zLOuvSgL$V^0zFNT4-dbf4g>^fnR3Pmo_pt|=~_z(CvL7vu7zJgUv-_B`7~_#`7hi{ zN*W|jEBshKmPYYK#bGg~W#5%kBm8k;*TlxOOP_iEmEi|7DA(b>%LCfS5W&g+6GYu2 ze@2;Z(Y^Vj<&Gfdh#EK-MU562i?zf)AKI5UI3arkQK!9kVs?8f9{skj<;PT{#aq_J z^z!+Z!7Q~}_DCDGkd=Na4PM_2T;qdQ5q!a)HOp%9jQ)NNA%1mqd1Ui_A$WAT?^A|s?(+e$ZPY=UY7BnHBGp2xCcUVOjQ}z9T zk&H1{q@+pgS4t?5Hh^_P9~zyo07icgm1^YI2e#sQfp~$&RrB9w7*qstzIKV)pa!CT zsN9Ujf(XN*>cFhnTl<=`yU$oh(kJ>(>E_0++tr)i4#+Z@Ph*2uI>5YB+s#BD9CrVA z<9_PE5IusR3*;p@}y;RtWJP=I{qlM6I*BIaZaGUT(ac$zP z(z(InGrUx@T7{66J`e=oZU?jvAv#!1|E5~rzm2mtPPQ25oY?1V>Dov{I~dd%({Zsk zEL{JxWm&!R5k%9g`*imVsHyr5bM6!&;4*D^VYOj?*|llN4Seyq=jsypOsA6I4K=lm zEMVrKlbbp#XdZXC@i@<1EY0s%B=&`DiwRo|2|H_{n`z{w>9=7n9Z@7P(s5LqMJ@j2 z;RT`#*g9VfHY{UiGf+gAyNq64`ZV-yOI6%_ulfj@iD;(T^8l=}uV52c)E&6MTw(!`pQy%c_N?;C8x|yp>DYN?d=SyPFb#>*R zEj=vQB=etFg8&n!wjdP$Fag)7kK72r?{U@&p?lPG*MqdYJlVT|Li*-W(1S*Jy ze}ZbyW>scw?<UxPEegm&3Op4W;FCZG$Eoz*1Wy&LB$((109A7YXGzAz-;-YkqODwkO?I2IP?rXlQPpFuc zgM-j$ZVMK$MMVzdv*Fg{25@zRh+o#7Aq4YI6@xN2JW6^$dnq3m{pw<# zV9SSHba7iH!OrLAV#)}^@+#ZEaJ}7#{iJ!0kzy`yr;)d96+2rO7vtO)@~w~4ou>KW zT+I%?EaA2OfCB_%Kp6Zu6y5(^$R`Uu@5LH<5KARbN}$qKy*t4K(O3!sid?^f>iN6u zZ=lxaaB$3QpSNO<6RoWq@04q;2u9ijJT!c9`PF?L@-W9Ci;IKc1D*0T8ETpu!Suji zx~Fp9&lsojHm+fgqtzsPVuZv0{lbUO;d3`p+Oa1KI$tL8SGg(;1&B^s5$30sl34Lg z+5QseBB87^ct9A6b&aRlguflR9f~SopJLz;tOFtbZ{aG`TP*Lr+J0is$H!j2Kv{RI zxb#m{;u}4-uuyM$CwuRtBe0Ti#^_Y8{q|c)7_p`#{?)|zOcZ++)7#?0b+5wIA9&FH ziBoPAJu?e%>BJOnuE34DGrLh}1~5aYGuKs~-@40mu8ZiM91@wBMO@{}?JDJQ;>?=0 z!PyHB5L|@Z!Yn_(P3iZx)HG_$UV&lW*2Ue($QUPw{QDS3)DK}gWB`o*e#2Gv(P(_} zebKW`{z1xnvzc*5o{q65BNw;5p2S+$Nex(Q2Wv|BoKi}KhLzJfRrZfCiqh2;MczG~ z+Se_JNO3=TXfBk4d8bSiTbLE7?Q)br2L#d?LwX*@a_{)fE8h8|l?$@8#c_|XJB#d% zzk%Tz6_~ZLWT)hh#Oy}*5cj7J49HAn*CFNbvL@HZ-QMI@OWlzF7_$Y*2lPYY;1Sw* z@U`@h=@nvDKZX?&>?TL}E7BG#>ZohBB-hjiNPc+>cN?h`EC?TgH2FbJHWbDRDB41W!UtIjwEBXmyItTSo$klQxGHhsJ05?#;X9RVx&)C$}b$;*G#PsB$wGOkM^quHZFHq781O#8`Wp{WCIB0(5w1+cOI&%kBL5h zv-8_3s`M?4DKbE3;x1t-x4pP8bxTkoq`J7 zY17iwvNtw6^nw2$+?HN+D)!Y&g1+~X+XofY1Bir8zBZ+cdzOXPvqpxj(d)?@B^C(1 zv?9h?UsTC_M|e{=A$dnsYHfe_sszr}v`+(&1`dMgNAwS&f=5^W(GMlGtRU3@1}w zE)rT%asrrl%IF36UyR{)3gp=ZMW@e}BBpR+gpc*Q&w&8AoLm+$?A=h?c{oh$R<~7FRp!fd(#baouI-=gNK8)1g8#0 zf4}XhU$@C$kn2=J=D{Slh%f%6Ri%%Q{>*JRW$1%%)tE{McsYD6g(dQldYR)gma1Hy zja5}A7d04R&Asp$m4`}^-sNGF9+-D(ErHxPT`N#|;h>!JL741}(%Z{rK(?Gq@Ir-8 zz=F7e%&%upBNCU&STSFVas3gyqML%GTZJL>ol>l{lCaZ)aU%74l&!6!}_)&aHY$1cU~a9|MrZxfUe+(Gs5MexOuOw9k=-EYWEyn2JiOGAB4~WgmFau1SW`qe}bu8X=(d?9_IAM3 zKCd;z-LkeVH8q|pH+Plw&fe&QZ^0`%!cla8zFnrHdVmdh#yQQw zAzX({{1aSc(i0CH*u@Jok|b!}nr*d`e_!UjH*v|;mBr2{*IKXS2(Bl}AFo>tT5GXr zT4WdK>WZ1}v2r3l(l+KoP#?QV&<`>WPDktvL18>lMNsRgYB;d6&S?F631zJJLa@^to47zzNJdwf!8-^bJ5%=%~dM zSW{;-@;ratm?)&%TXEG<7;v9@F20Y2TzFy7H!tb!dNM-dHIlrak8Zn67joPCNO<^2SKZFTrO1DGzVY|ttxN~ zLrj3t-@}z+pryHKKEyKp()^8>5Lq_CGZlq3^;gIbgU%zdGG9FQ9+D^!lzpMi z-58>BT~{HcDnKb|Q*x#D%>*VMTJV3ur32zDItDP~!~Qf!-3893R{5m*_GJ*+;8)Yh zf0nu_MPT&WH#ACCNZTHFB}oV;I(cDVtHfJ>T3nzd3^Vd&EJC{z@pI(Xw5*@8*sCBv;_%4+RXKT=IppqL)WFZt4};J=Of-<+f_bO%3qbd5Z6jMVi=&7FlskDg ztX{;>MA{Lq_1voCLGsppliz2q_$xbT*c$0vuYWP;0%l0(FK(SMwyX~Ja4Bv7zMxUR zbkkpV87xE+8)sE+alV7+7aWs601A>;|D3&q2Dkx7HUU%rT^wlDV*C$ zDj!DoqwQ>l4MfH@m;}Q3L1nWo1DwfarRl+D}bTZ^t)7PE!w%6BstgNMazMK2* z1?A~$D3A`)*UYs0B~p#%y}7|FQuv5j4_AU15?I)$7&rk7;sK2QeiJ&ow{&hQ5Dtb6 zh3~Beaj3td@SeZuX*I>F>2f8~xZya}^&(=qc%c;GRpnhd5sz;KELs~F^^Z(4`ZVa0 zZpuIRfpXDLt*WD|Z%5rbL2}Jm&%g#WS$~$+rZ9M}>j8^rwf;WTZf_#7lqWyQ?a+Qp+Y-`zDL!S! zFUeU9)%>ySGuCytZ09n$nvS?zw_w$|Q&Tt3#o#46NBp^5S;PZ#xF`yxBBH`c{|7+- zCeBNuuoNF*1f)55R$>~e&Ao~EvM%8~=%nZ~F2s^&ZV8mi1AwRqjjAp)zpb8ZBHMrA zcRqyYs=l93wPg^eDzcoj*o-`wcgk^Rps5AZf*t7yP{;bSNhgnT%NGya=u;v}!i8HT zp9&>}-0I=Snc-TZ+f{yHqeGss`($`uJK`1>xAP%sS+tgw?SbeV0{@XN)`G+r!Hb70oig zcSwc46UO1nZa15RY-H?H3><<5^mB-Ze*&x6$+d6t5;M|6lo9EL7x>wYi|?gVA5Rynt^`j=k#X%4^dhF=K0n9TNK<=6m%i9_ zdJpel*UlQHGT7FBwrKEMk7KGOzx4SBbljmG&<0P3nmI%sjQ)N--FQ8ZPM2Iy-D-;A zQTot$wD`r>>()Bf0%!tq-@>?>9)Op_r4q-Eo-IL`x_iL8_b8NFBf+p|40=&lE?`HT z<3)O4Ms^?Y(CZ=K%!~UNi#&WS zljjIjK87c9ef#R_DGFKAtS2|UrW_+U@_d?}qVFQ3&^CYsOxMoFThnx^)EX8!STyB1 zvRay!OyGY|jzZJaQum~W!|&)lWKk-n0n`Wrl=va980sd@EzI!+lwLoiwa!J%5l=A_ zV=XOE(CYY}l=;4O$()?y9OX_m40&4*w>V>k{?o$Z<;Rp5ukSk*5lCvXa;vE4dV zli4|s0((TJxCcEzz9h*M$MF+>n2|;37+TF*cf^ElR=y|bPiYA=Iw8Vi=J-^!ukk7~ zVsy&Jt?zxIcS^p6gns{y^Ci~poTvS|I26f-tm9+0`X4_f@4PkQcyqXmCIm#m=e~ z&h2>ztVhg6M>N9FUQA`$x+F4xFenr>*1DCk{5)7+oIR~+pjh_#LX`K^Q~FOEhBekv zMm_oYG3$hOp??Hzpsj#;OdbpltPMB^VD$GpWgMxoMdkX#eQ&}m%2Y)R52dWBY`ADN zZOQgQ*9<0XNJzkU%JhSp-qMU~3$YqK>xIK#3qm{Z1}!T-%Z!Pu8c(YcR{%Vm^0=Mq zHwD*qz=j59)}Unu9~Q4w?8TxQuchcHof?>iURuNe<7y zy(wgP;{u0IkI zHV)>UQg^hCtu=KZ(R&aCJEQvS#=EA^Q9?%lIxC1detC(Iz8*8)mA{DWUSrE!lQ6Zp z5BJ+{uvrW%%wB&Kv|eg`BlRtn&ziT4Na+os)Qx%vNl5gf!byXNt}`vknyx}yMbVi*7MWHkb=6pdeoDk=5=}fV zG5%OG&h~Ljg!#UE1~)$@8f@_SumV4{yU-6=IEL!9p?_vp{!8wuIp<3*hH)A9ZjZ+^ zg|@A}u@km9#@wd6MQ%IA`%?T}M}P^tYd;-@QD$zEw~(34zpw6AyHb-5!{o!<*d(d0 zK?5ravBN_^R)EUOQ`!`it_FyyTLFXrin&kOW+T6m-P*>Z^%mQi-eJrmhw^RvylU`kLnid;Lmm!-9|MYx>ovNsf&4^Zb z=FR$BcPY?0LDNFVjE9BgM9R;-7x1OWdi3J$W*?c}YMR{D-15ZbAC3> ziGa(-Y+T1wURh@knXzw5Y|2D@_^~l?b)+#-^7YK0+h~D(={}hnlHgFVfLU7_ogBR* zDEc33AO6rl!j+@rZ1~jv`g5Olm8nTyzj?Fl?4_g$3Fzbn)u~NSP6gR1VwLz{mZ&&E zPh@Nvl(GH$1NgCPbQR8O4lSNf{oLHewLmd^69C^aUmV23vCTryodWg!joat*Tg+DJ z1_#D>FJa}~DW-K`?{k;1Fw@pH&^_ITj&~-e4K2V-i~{4fxGr1fMU8IsVec%>W|e8V zCN_H66MnAK5dW6Ts#(nTC48aC#PhS9!?ToA@{acfp1`U1!JyIbq44dz7dh|JL?^6Z zT(!5HrMuFJHa~t*r0*pK)hC*#I#aY@-YIFK1r-H7lqQ^K51kp`yNu%ZBVAtHKTan^ zOxIbg(65zI^eKNf>tl4ZI60!6@cRM2&hMGt=MK5Iy0_+anMU4>j8KR+P>T`?K$e37 z)~ImO;Nei3K-9tL?@yo%C<0^Xjc5uWa5zVpd(qP^x<4GUrxmE^nUH8CYcg9eV z$Jlj^tUy9Ulj#-~F_l&gHOKb`8EfR`p)UM2xw^aLX=J4<461^K7#jpOH=lEBF!bKM zJ^7BdCM%XoIyM_)^BiCU{g60#JOBxjBmWi!YEzP8r%h=Wq2u58$oFi?6VT$N9Bl}9 zTFXeW?i=JbIs$3t);Y$eYuNQ)rMboI%f902tJYPe-wk2aNB^d|>1zry4Ng0Dpxp!Y z9kJCtvNFN-jWcRH-n&nz)UsStUOT1enuZLpQs|5-HS=#cs$ZDY+k8_*g-KCM9?Abw zt-5$_{}u~snW0mp{-f=;z!{2A zn0W^^KxNNE$ngq6?~ljxSaoVp)&P7(-68ewt;oFfxMfiFvMKq`s;E~N+K?Un)~h}l z?<3B-Rf2h^Rur%dv_S3I!%YYBXwC?f2lseWJFHSwoR#9!o@ze_(hUrpWI7H~T3=Lt zB$?)@RqQka)-*Dw@~sa&atPR@BuJZ$8CYsHs0_BWa!m8Lq0k2UA@LKCAo^hR_uI}? zlU<=A{2Y(PO(Jb9-@u=)xJj?vw52`1^1ecQBmpW>9fDNMM%k~7IMQ-n+2O|f(XlF{ z7R7{bcu&G*A9$1)ym17XnWtA2C^cvdiaSF;AfWzFI-&PWobe+ref3%UFjLne24%tp zESfZo`?)H{HiI~Bv}{UmpK@%D-(+xdq$Y2Q0_OKzJyCa~0R_04|an^(uIM+uKsR9NB1i7lev z>Z%_zc+?mMl@R3vGh<+P p!G$bNU0*`r5vB^3Z|Jvkf<9cfPyL3D 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 z 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 literal 0 HcmV?d00001 diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 6deacb51..e613ebd2 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -70,7 +70,7 @@ public Set getConnected() { } private static final class WantResult { - public final CompletableFuture result = new CompletableFuture(); + public final CompletableFuture result = new CompletableFuture<>(); public final long creationTime; public WantResult(long creationTime) { @@ -78,7 +78,6 @@ public WantResult(long creationTime) { } } - private Map recentSentWants(PeerId peer) { Map recent = recentWantsSent.get(peer); if (recent == null) { diff --git a/src/test/java/org/peergos/BitswapMirrorTest.java b/src/test/java/org/peergos/BitswapMirrorTest.java index 0c29e890..ed537568 100644 --- a/src/test/java/org/peergos/BitswapMirrorTest.java +++ b/src/test/java/org/peergos/BitswapMirrorTest.java @@ -29,6 +29,7 @@ public void mirrorTree() throws IOException { IdentifyBuilder.addIdentifyProtocol(node1); IPFS kubo = new IPFS("localhost", 5001); Multiaddr kuboAddress = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); + preloadBlocksToKubo(kubo); // Multiaddr kuboAddress = Multiaddr.fromString("/ip4/172.104.157.121/tcp/4001/p2p/QmVdFZgHnEgcedCS2G2ZNiEN59LuVrnRm7z3yXtEBv2XiF"); node1.getAddressBook().setAddrs(kuboAddress.getPeerId(), 0, kuboAddress).join(); Kademlia dht1 = builder1.getWanDht().get(); @@ -43,19 +44,26 @@ public void mirrorTree() throws IOException { Set rawToGet = new HashSet<>(); toGet.add(new Want(Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"))); long t1 = System.currentTimeMillis(); + Map blocks = new HashMap<>(); while (true) { Bitswap bitswap1 = builder1.getBitswap().get(); // BitswapController bc1 = bitswap1.dial(node1, kuboAddress).getController().join(); List cborBlocks = bitswap1 .get(new ArrayList<>(toGet), node1, Set.of(kuboAddress.getPeerId()), false).stream() .map(f -> f.join()) - .map(h -> h.block) + .map(h -> { + blocks.put(h.hash, h.block); + return h.block; + }) .map(CborObject::fromByteArray) .collect(Collectors.toList()); List rawBlocks = bitswap1 .get(new ArrayList<>(rawToGet), node1, Set.of(kuboAddress.getPeerId()), false).stream() .map(f -> f.join()) - .map(h -> h.block) + .map(h -> { + blocks.put(h.hash, h.block); + return h.block; + }) .collect(Collectors.toList()); toGet.clear(); rawToGet.clear(); @@ -72,8 +80,28 @@ public void mirrorTree() throws IOException { } long t2 = System.currentTimeMillis(); System.out.println("Mirror took " + (t2-t1) + "mS"); + Assert.assertTrue(blocks.size() == 6745); } finally { node1.stop(); } } + + private static void preloadBlocksToKubo(IPFS kubo) throws IOException { + DataInputStream din = new DataInputStream(new FileInputStream("blocks.bin")); + Map blocks = new HashMap<>(); + while (true) { + try { + int cidSize = din.readInt(); + byte[] rawCid = din.readNBytes(cidSize); + int len = din.readInt(); + byte[] b = din.readNBytes(len); + blocks.put(Cid.cast(rawCid), b); + } catch (IOException e) { + break; + } + } + for (Map.Entry e : blocks.entrySet()) { + kubo.block.put(e.getValue(), Optional.of(e.getKey().codec == Cid.Codec.Raw ? "raw" : "dag-cbor")); + } + } } From afd2f129493babd703d37cb01a62aa47a2bf9238 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 25 Oct 2023 15:09:55 +0100 Subject: [PATCH 088/218] Delete kubo pin file --- kubo-pin.sh | 4 ---- 1 file changed, 4 deletions(-) delete mode 100755 kubo-pin.sh diff --git a/kubo-pin.sh b/kubo-pin.sh deleted file mode 100755 index e902aa87..00000000 --- a/kubo-pin.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh -export PATH=$PATH:$PWD/kubo/ -ipfs swarm connect /ip4/172.104.157.121/tcp/4001/p2p/QmVdFZgHnEgcedCS2G2ZNiEN59LuVrnRm7z3yXtEBv2XiF -time ipfs pin add zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg From a9170f559af06e47170f934ca7da38e6315217dd Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 25 Oct 2023 15:27:54 +0100 Subject: [PATCH 089/218] Set version to 0.6.0 --- src/main/java/org/peergos/net/APIHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index b3b3898f..d05fa797 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -13,7 +13,7 @@ public class APIHandler extends Handler { public static final String API_URL = "/api/v0/"; - public static final Version CURRENT_VERSION = Version.parse("0.0.1"); + public static final Version CURRENT_VERSION = Version.parse("0.6.0"); private static final Logger LOG = Logging.LOG(); private static final boolean LOGGING = true; From 0332dd40dc8f5769efe33888d3a577abdee7ef7b Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 25 Oct 2023 16:08:15 +0100 Subject: [PATCH 090/218] Update version in pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f623d6d2..2be49e0f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.5.0 + v0.6.0 UTF-8 From d59dfb17b59af58aeba6cb58ebeb113813d65cd7 Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 29 Oct 2023 22:04:54 +0000 Subject: [PATCH 091/218] Add prometheus bandwidth metrics to bitswap and kademlia --- .../protocol/bitswap/BitswapConnection.java | 6 ++- .../protocol/bitswap/BitswapEngine.java | 8 +++- .../protocol/bitswap/BitswapProtocol.java | 33 +++++++++++++--- .../peergos/protocol/dht/KademliaEngine.java | 16 ++++++-- .../protocol/dht/KademliaProtocol.java | 38 ++++++++++++++++--- 5 files changed, 83 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapConnection.java b/src/main/java/org/peergos/protocol/bitswap/BitswapConnection.java index e45d2ddb..24e417ba 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapConnection.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapConnection.java @@ -1,6 +1,7 @@ package org.peergos.protocol.bitswap; import io.libp2p.core.Stream; +import io.prometheus.client.*; import kotlin.*; import org.peergos.protocol.bitswap.pb.*; @@ -9,14 +10,17 @@ public class BitswapConnection implements BitswapController { private final Stream conn; + private final Counter sentBytes; - public BitswapConnection(Stream conn) { + public BitswapConnection(Stream conn, Counter sentBytes) { this.conn = conn; + this.sentBytes = sentBytes; } @Override public void send(MessageOuterClass.Message msg) { conn.writeAndFlush(msg); + sentBytes.inc(msg.getSerializedSize()); } @Override diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index e613ebd2..12850c36 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -6,6 +6,7 @@ import io.libp2p.core.*; import io.libp2p.core.Stream; import io.libp2p.core.multiformats.*; +import io.prometheus.client.*; import org.peergos.*; import org.peergos.blockstore.*; import org.peergos.protocol.bitswap.pb.*; @@ -122,7 +123,7 @@ private static byte[] prefixBytes(Cid c) { } } - public void receiveMessage(MessageOuterClass.Message msg, Stream source) { + public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter sentBytes) { List presences = new ArrayList<>(); List blocks = new ArrayList<>(); @@ -263,7 +264,10 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source) { if (presences.isEmpty() && blocks.isEmpty() && wants.isEmpty()) return; - buildAndSendMessages(wants, presences, blocks, source::writeAndFlush); + buildAndSendMessages(wants, presences, blocks, reply -> { + sentBytes.inc(reply.getSerializedSize()); + source.writeAndFlush(reply); + }); } public void buildAndSendMessages(List wants, diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java index 11c0c52d..0ed63545 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java @@ -2,6 +2,7 @@ import io.libp2p.core.*; import io.libp2p.protocol.*; +import io.prometheus.client.*; import org.jetbrains.annotations.*; import org.peergos.protocol.bitswap.pb.*; @@ -9,6 +10,22 @@ public class BitswapProtocol extends ProtobufProtocolHandler { + private static final Counter initiatorReceivedBytes = Counter.build() + .name("bitswap_initiator_received_bytes") + .help("Total received bytes in bitswap initiator") + .register(); + private static final Counter initiatorSentBytes = Counter.build() + .name("bitswap_initiator_sent_bytes") + .help("Total sent bytes in bitswap initiator") + .register(); + private static final Counter responderReceivedBytes = Counter.build() + .name("bitswap_responder_received_bytes") + .help("Total received bytes in bitswap responder") + .register(); + private static final Counter responderSentBytes = Counter.build() + .name("bitswap_responder_sent_bytes") + .help("Total sent bytes in bitswap responder") + .register(); private final BitswapEngine engine; public BitswapProtocol(BitswapEngine engine) { @@ -19,31 +36,35 @@ public BitswapProtocol(BitswapEngine engine) { @NotNull @Override protected CompletableFuture onStartInitiator(@NotNull Stream stream) { - BitswapConnection conn = new BitswapConnection(stream); + BitswapConnection conn = new BitswapConnection(stream, initiatorSentBytes); engine.addConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); - stream.pushHandler(new MessageHandler(engine)); + stream.pushHandler(new MessageHandler(engine, initiatorSentBytes, initiatorReceivedBytes)); return CompletableFuture.completedFuture(conn); } @NotNull @Override protected CompletableFuture onStartResponder(@NotNull Stream stream) { - BitswapConnection conn = new BitswapConnection(stream); + BitswapConnection conn = new BitswapConnection(stream, responderSentBytes); engine.addConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); - stream.pushHandler(new MessageHandler(engine)); + stream.pushHandler(new MessageHandler(engine, responderSentBytes, responderReceivedBytes)); return CompletableFuture.completedFuture(conn); } class MessageHandler implements ProtocolMessageHandler { private BitswapEngine engine; + private final Counter sentBytes, receivedBytes; - public MessageHandler(BitswapEngine engine) { + public MessageHandler(BitswapEngine engine, Counter sentBytes, Counter receivedBytes) { this.engine = engine; + this.sentBytes = sentBytes; + this.receivedBytes = receivedBytes; } @Override public void onMessage(@NotNull Stream stream, MessageOuterClass.Message msg) { - engine.receiveMessage(msg, stream); + receivedBytes.inc(msg.getSerializedSize()); + engine.receiveMessage(msg, stream, sentBytes); } } } diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 499b6119..5a3470d0 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -8,6 +8,7 @@ import io.libp2p.core.*; import io.libp2p.core.Stream; import io.libp2p.core.multiformats.*; +import io.prometheus.client.*; import org.peergos.*; import org.peergos.blockstore.*; import org.peergos.protocol.dht.pb.*; @@ -67,13 +68,14 @@ public List getKClosestPeers(byte[] key) { .collect(Collectors.toList()); } - public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { + public void receiveRequest(Dht.Message msg, PeerId source, Stream stream, Counter sentBytes) { switch (msg.getType()) { case PUT_VALUE: { Optional mapping = IPNS.validateIpnsEntry(msg); if (mapping.isPresent()) { ipnsStore.put(mapping.get().publisher, mapping.get().value); stream.writeAndFlush(msg); + sentBytes.inc(msg.getSerializedSize()); } break; } @@ -90,7 +92,9 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { .stream() .map(PeerAddresses::toProtobuf) .collect(Collectors.toList())); - stream.writeAndFlush(builder.build()); + Dht.Message reply = builder.build(); + stream.writeAndFlush(reply); + sentBytes.inc(reply.getSerializedSize()); break; } case ADD_PROVIDER: { @@ -117,7 +121,9 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { .stream() .map(PeerAddresses::toProtobuf) .collect(Collectors.toList())); - stream.writeAndFlush(builder.build()); + Dht.Message reply = builder.build(); + stream.writeAndFlush(reply); + sentBytes.inc(reply.getSerializedSize()); break; } case FIND_NODE: { @@ -126,7 +132,9 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { .stream() .map(PeerAddresses::toProtobuf) .collect(Collectors.toList())); - stream.writeAndFlush(builder.build()); + Dht.Message reply = builder.build(); + stream.writeAndFlush(reply); + sentBytes.inc(reply.getSerializedSize()); break; } case PING: {break;} // Not used any more diff --git a/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java b/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java index 1f8e2feb..49429bd6 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java @@ -2,12 +2,30 @@ import io.libp2p.core.*; import io.libp2p.protocol.*; +import io.prometheus.client.*; import org.jetbrains.annotations.*; import org.peergos.protocol.dht.pb.Dht; import java.util.concurrent.*; public class KademliaProtocol extends ProtobufProtocolHandler { + + private static final Counter initiatorReceivedBytes = Counter.build() + .name("kademlia_initiator_received_bytes") + .help("Total received bytes in kademlia protocol initiator") + .register(); + private static final Counter initiatorSentBytes = Counter.build() + .name("kademlia_initiator_sent_bytes") + .help("Total sent bytes in kademlia protocol initiator") + .register(); + private static final Counter responderReceivedBytes = Counter.build() + .name("kademlia_responder_received_bytes") + .help("Total received bytes in kademlia protocol responder") + .register(); + private static final Counter responderSentBytes = Counter.build() + .name("kademlia_responder_sent_bytes") + .help("Total sent bytes in kademlia protocol responder") + .register(); public static final int MAX_MESSAGE_SIZE = 1024*1024; private final KademliaEngine engine; @@ -21,7 +39,7 @@ public KademliaProtocol(KademliaEngine engine) { @Override protected CompletableFuture onStartInitiator(@NotNull Stream stream) { engine.addOutgoingConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); - ReplyHandler handler = new ReplyHandler(stream); + ReplyHandler handler = new ReplyHandler(stream, initiatorSentBytes, initiatorReceivedBytes); stream.pushHandler(handler); return CompletableFuture.completedFuture(handler); } @@ -30,7 +48,7 @@ protected CompletableFuture onStartInitiator(@NotNull Stream @Override protected CompletableFuture onStartResponder(@NotNull Stream stream) { engine.addIncomingConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); - IncomingRequestHandler handler = new IncomingRequestHandler(engine); + IncomingRequestHandler handler = new IncomingRequestHandler(engine, responderSentBytes, responderReceivedBytes); stream.pushHandler(handler); return CompletableFuture.completedFuture(handler); } @@ -38,25 +56,31 @@ protected CompletableFuture onStartResponder(@NotNull Stream class ReplyHandler implements ProtocolMessageHandler, KademliaController { private final CompletableFuture resp = new CompletableFuture<>(); private final Stream stream; + private final Counter sentBytes, receivedBytes; - public ReplyHandler(Stream stream) { + public ReplyHandler(Stream stream, Counter sentBytes, Counter receivedBytes) { this.stream = stream; + this.sentBytes = sentBytes; + this.receivedBytes = receivedBytes; } @Override public CompletableFuture rpc(Dht.Message msg) { stream.writeAndFlush(msg); + sentBytes.inc(msg.getSerializedSize()); return resp; } @Override public CompletableFuture send(Dht.Message msg) { stream.writeAndFlush(msg); + sentBytes.inc(msg.getSerializedSize()); return CompletableFuture.completedFuture(true); } @Override public void onMessage(@NotNull Stream stream, Dht.Message msg) { + receivedBytes.inc(msg.getSerializedSize()); resp.complete(msg); stream.closeWrite(); } @@ -74,14 +98,18 @@ public void onException(@Nullable Throwable cause) { class IncomingRequestHandler implements ProtocolMessageHandler, KademliaController { private final KademliaEngine engine; + private final Counter sentBytes, receivedBytes; - public IncomingRequestHandler(KademliaEngine engine) { + public IncomingRequestHandler(KademliaEngine engine, Counter sentBytes, Counter receivedBytes) { this.engine = engine; + this.sentBytes = sentBytes; + this.receivedBytes = receivedBytes; } @Override public void onMessage(@NotNull Stream stream, Dht.Message msg) { - engine.receiveRequest(msg, stream.remotePeerId(), stream); + receivedBytes.inc(msg.getSerializedSize()); + engine.receiveRequest(msg, stream.remotePeerId(), stream, sentBytes); } @Override From 1e808bd3e7d27cb7629f85c3f74a984174d1ecf8 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 30 Oct 2023 08:47:54 +0000 Subject: [PATCH 092/218] update s3 prometheus metric names (#68) * rename metrics to align with existing ipfs nucleus metrics --- .../peergos/blockstore/s3/S3Blockstore.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index a8ff95e0..333467c0 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -35,47 +35,44 @@ public class S3Blockstore implements Blockstore { private static final Logger LOG = Logging.LOG(); private static final Histogram readTimerLog = Histogram.build() - .labelNames("filesize") - .name("block_read_seconds") + .name("ipfs_block_read_seconds") .help("Time to read a block from immutable storage") .exponentialBuckets(0.01, 2, 16) .register(); private static final Histogram writeTimerLog = Histogram.build() - .labelNames("filesize") - .name("s3_block_write_seconds") + .name("ipfs_block_write_seconds") .help("Time to write a block to immutable storage") .exponentialBuckets(0.01, 2, 16) .register(); private static final Counter blockHeads = Counter.build() - .name("s3_block_heads") - .help("Number of block heads to S3") + .name("ipfs_block_heads_total") + .help("The total number of S3 block head requests") .register(); private static final Counter blockGets = Counter.build() - .name("s3_block_gets") - .help("Number of block gets to S3") + .name("ipfs_block_gets_total") + .help("The total number of S3 block get requests") .register(); private static final Counter failedBlockGets = Counter.build() - .name("s3_block_get_failures") + .name("ipfs_block_get_failures") .help("Number of failed block gets to S3") .register(); private static final Counter blockPuts = Counter.build() - .name("s3_block_puts") + .name("ipfs_block_puts_total") .help("Number of block puts to S3") .register(); private static final Histogram blockPutBytes = Histogram.build() - .labelNames("size") - .name("s3_block_put_bytes") + .name("ipfs_block_put_bytes") .help("Number of bytes written to S3") .exponentialBuckets(0.01, 2, 16) .register(); private static final Counter getRateLimited = Counter.build() - .name("s3_get_rate_limited") + .name("ipfs_get_rate_limited_total") .help("Number of times we get a http 429 rate limit response during a block get") .register(); private static final Counter rateLimited = Counter.build() - .name("s3_rate_limited") + .name("ipfs_rate_limited_total") .help("Number of times we get a http 429 rate limit response") .register(); From 434453a4c700dc2c2267dff50b54d8ef84b83d30 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 30 Oct 2023 10:12:28 +0000 Subject: [PATCH 093/218] add label to Counter + Histogram metrics (#70) * add label to Counter + Histogram metrics * remove label from counter + revert label name for histogram --- src/main/java/org/peergos/blockstore/s3/S3Blockstore.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index 333467c0..e050b76e 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -35,11 +35,13 @@ public class S3Blockstore implements Blockstore { private static final Logger LOG = Logging.LOG(); private static final Histogram readTimerLog = Histogram.build() + .labelNames("filesize") .name("ipfs_block_read_seconds") .help("Time to read a block from immutable storage") .exponentialBuckets(0.01, 2, 16) .register(); private static final Histogram writeTimerLog = Histogram.build() + .labelNames("filesize") .name("ipfs_block_write_seconds") .help("Time to write a block to immutable storage") .exponentialBuckets(0.01, 2, 16) @@ -61,6 +63,7 @@ public class S3Blockstore implements Blockstore { .help("Number of block puts to S3") .register(); private static final Histogram blockPutBytes = Histogram.build() + .labelNames("size") .name("ipfs_block_put_bytes") .help("Number of bytes written to S3") .exponentialBuckets(0.01, 2, 16) From 463a3f7cb17a195e764cc20fa9bbd77a5b5d7a4a Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Oct 2023 16:47:37 +0000 Subject: [PATCH 094/218] Allow bitswap protocol id and max block size to be varied --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 +- src/main/java/org/peergos/HostBuilder.java | 2 +- .../java/org/peergos/protocol/bitswap/Bitswap.java | 5 +++++ .../peergos/protocol/bitswap/BitswapEngine.java | 14 ++++++++++---- .../peergos/protocol/bitswap/BitswapProtocol.java | 2 +- src/test/java/org/peergos/KuboPingTest.java | 2 +- src/test/java/org/peergos/KuboTest.java | 2 +- src/test/java/org/peergos/PingTest.java | 12 ++++++------ 8 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 5c67263f..c9d7df94 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -201,7 +201,7 @@ public static EmbeddedIpfs build(RecordStore records, Kademlia dht = new Kademlia(new KademliaEngine(ourPeerId, providers, records, blockstore), false); CircuitStopProtocol.Binding stop = new CircuitStopProtocol.Binding(); CircuitHopProtocol.RelayManager relayManager = CircuitHopProtocol.RelayManager.limitTo(builder.getPrivateKey(), ourPeerId, 5); - Bitswap bitswap = new Bitswap(new BitswapEngine(blockstore, authoriser)); + Bitswap bitswap = new Bitswap(new BitswapEngine(blockstore, authoriser, Bitswap.MAX_MESSAGE_SIZE)); Optional httpHandler = handler.map(HttpProtocol.Binding::new); List protocols = new ArrayList<>(); diff --git a/src/main/java/org/peergos/HostBuilder.java b/src/main/java/org/peergos/HostBuilder.java index a9c3f58b..11af3299 100644 --- a/src/main/java/org/peergos/HostBuilder.java +++ b/src/main/java/org/peergos/HostBuilder.java @@ -121,7 +121,7 @@ public static HostBuilder create(int listenPort, new Ping(), new AutonatProtocol.Binding(), new CircuitHopProtocol.Binding(relayManager, stop), - new Bitswap(new BitswapEngine(blocks, authoriser)), + new Bitswap(new BitswapEngine(blocks, authoriser, Bitswap.MAX_MESSAGE_SIZE)), dht)); } diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index 39fcf5de..3c12fbb0 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -31,6 +31,11 @@ public Bitswap(BitswapEngine engine) { this.engine = engine; } + public Bitswap(String protocolId, BitswapEngine engine) { + super(protocolId, new BitswapProtocol(engine)); + this.engine = engine; + } + public void setAddressBook(AddressBook addrs) { engine.setAddressBook(addrs); this.addrs = addrs; diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 12850c36..40c88617 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -24,6 +24,7 @@ public class BitswapEngine { private static final Logger LOG = Logging.LOG(); private final Blockstore store; + private final int maxMessageSize; private final ConcurrentHashMap localWants = new ConcurrentHashMap<>(); private final ConcurrentHashMap persistBlocks = new ConcurrentHashMap<>(); private final ConcurrentHashMap blockHaves = new ConcurrentHashMap<>(); @@ -34,9 +35,14 @@ public class BitswapEngine { private final BlockRequestAuthoriser authoriser; private AddressBook addressBook; - public BitswapEngine(Blockstore store, BlockRequestAuthoriser authoriser) { + public BitswapEngine(Blockstore store, BlockRequestAuthoriser authoriser, int maxMessageSize) { this.store = store; this.authoriser = authoriser; + this.maxMessageSize = maxMessageSize; + } + + public int maxMessageSize() { + return maxMessageSize; } public void setAddressBook(AddressBook addrs) { @@ -166,7 +172,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter .setData(ByteString.copyFrom(block.get())) .build(); int blockSize = blockP.getSerializedSize(); - if (blockSize + messageSize > Bitswap.MAX_MESSAGE_SIZE) { + if (blockSize + messageSize > maxMessageSize) { buildAndSendMessages(wants, presences, blocks, source::writeAndFlush); wants = new ArrayList<>(); presences = new ArrayList<>(); @@ -280,7 +286,7 @@ public void buildAndSendMessages(List for (int i=0; i < wants.size(); i++) { MessageOuterClass.Message.Wantlist.Entry want = wants.get(i); int wantSize = want.getSerializedSize(); - if (wantSize + messageSize > Bitswap.MAX_MESSAGE_SIZE) { + if (wantSize + messageSize > maxMessageSize) { sender.accept(builder.build()); builder = MessageOuterClass.Message.newBuilder(); messageSize = 0; @@ -291,7 +297,7 @@ public void buildAndSendMessages(List for (int i=0; i < presences.size(); i++) { MessageOuterClass.Message.BlockPresence presence = presences.get(i); int presenceSize = presence.getSerializedSize(); - if (presenceSize + messageSize > Bitswap.MAX_MESSAGE_SIZE) { + if (presenceSize + messageSize > maxMessageSize) { sender.accept(builder.build()); builder = MessageOuterClass.Message.newBuilder(); messageSize = 0; diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java index 0ed63545..9c5831a9 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java @@ -29,7 +29,7 @@ public class BitswapProtocol extends ProtobufProtocolHandler private final BitswapEngine engine; public BitswapProtocol(BitswapEngine engine) { - super(MessageOuterClass.Message.getDefaultInstance(), Bitswap.MAX_MESSAGE_SIZE, Bitswap.MAX_MESSAGE_SIZE); + super(MessageOuterClass.Message.getDefaultInstance(), engine.maxMessageSize(), engine.maxMessageSize()); this.engine = engine; } diff --git a/src/test/java/org/peergos/KuboPingTest.java b/src/test/java/org/peergos/KuboPingTest.java index 8e56b785..4d81bbac 100644 --- a/src/test/java/org/peergos/KuboPingTest.java +++ b/src/test/java/org/peergos/KuboPingTest.java @@ -22,7 +22,7 @@ public void runPingOverYamux() throws IOException { Host node1 = new HostBuilder() .generateIdentity() .listen(List.of(new MultiAddress("/ip4/0.0.0.0/tcp/" + TestPorts.getPort()))) - .addProtocols(List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))) + .addProtocols(List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))) .addMuxers(List.of(StreamMuxerProtocol.getYamux())) .build(); node1.start().join(); diff --git a/src/test/java/org/peergos/KuboTest.java b/src/test/java/org/peergos/KuboTest.java index 8f07414e..f21fb2b0 100644 --- a/src/test/java/org/peergos/KuboTest.java +++ b/src/test/java/org/peergos/KuboTest.java @@ -18,7 +18,7 @@ public class KuboTest { @Test public void getBlock() throws IOException { - Bitswap bitswap1 = new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))); + Bitswap bitswap1 = new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)); Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(bitswap1)); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/PingTest.java b/src/test/java/org/peergos/PingTest.java index 867a466b..7d99dcdc 100644 --- a/src/test/java/org/peergos/PingTest.java +++ b/src/test/java/org/peergos/PingTest.java @@ -21,8 +21,8 @@ public class PingTest { @Test public void runPing() { - Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); - Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); + Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); + Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); node2.start().join(); try { @@ -55,11 +55,11 @@ public void runPingEd25519ToRSA() { PrivKey node1Keys = Ed25519Kt.generateEd25519KeyPair().getFirst(); int node1Port = TestPorts.getPort(); Host node1 = build(node1Keys, node1Port, List.of(new Ping(), - new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); + new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); PrivKey node2Keys = RsaKt.generateRsaKeyPair(2048).getFirst(); int node2Port = TestPorts.getPort(); Host node2 = build(node2Keys, node2Port, List.of(new Ping(), - new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); + new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); node2.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); @@ -84,8 +84,8 @@ public void runPingEd25519ToRSA() { @Test public void replyIdentifyOnNewDial() { - Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); - Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true))))); + Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); + Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); node2.start().join(); From 560afc9a739331091db67e992a01e8f4134ba35b Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Oct 2023 20:16:22 +0000 Subject: [PATCH 095/218] Block aggressive bitswap peers (kubo) These peers flood us with irrelevant requests. --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 +- .../protocol/bitswap/BitswapEngine.java | 32 +++++++++++++++---- .../protocol/bitswap/BitswapProtocol.java | 4 +++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index c9d7df94..6a322497 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -201,7 +201,7 @@ public static EmbeddedIpfs build(RecordStore records, Kademlia dht = new Kademlia(new KademliaEngine(ourPeerId, providers, records, blockstore), false); CircuitStopProtocol.Binding stop = new CircuitStopProtocol.Binding(); CircuitHopProtocol.RelayManager relayManager = CircuitHopProtocol.RelayManager.limitTo(builder.getPrivateKey(), ourPeerId, 5); - Bitswap bitswap = new Bitswap(new BitswapEngine(blockstore, authoriser, Bitswap.MAX_MESSAGE_SIZE)); + Bitswap bitswap = new Bitswap(new BitswapEngine(blockstore, authoriser, Bitswap.MAX_MESSAGE_SIZE, true)); Optional httpHandler = handler.map(HttpProtocol.Binding::new); List protocols = new ArrayList<>(); diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 40c88617..ec2eefce 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -31,20 +31,32 @@ public class BitswapEngine { private final Map deniedWants = Collections.synchronizedMap(new LRUCache<>(10_000)); private final Map> recentBlocksSent = Collections.synchronizedMap(new LRUCache<>(100)); private final Map> recentWantsSent = Collections.synchronizedMap(new org.peergos.util.LRUCache<>(100)); + private final Map blockedPeers = Collections.synchronizedMap(new LRUCache<>(1_000)); + private final boolean blockAggressivePeers; private final Set connections = new HashSet<>(); private final BlockRequestAuthoriser authoriser; private AddressBook addressBook; - public BitswapEngine(Blockstore store, BlockRequestAuthoriser authoriser, int maxMessageSize) { + public BitswapEngine(Blockstore store, BlockRequestAuthoriser authoriser, int maxMessageSize, boolean blockAggressivePeers) { this.store = store; this.authoriser = authoriser; this.maxMessageSize = maxMessageSize; + this.blockAggressivePeers = blockAggressivePeers; + } + + public BitswapEngine(Blockstore store, BlockRequestAuthoriser authoriser, int maxMessageSize) { + this(store, authoriser, maxMessageSize, false); } + public int maxMessageSize() { return maxMessageSize; } + public boolean allowConnection(PeerId peer) { + return ! blockAggressivePeers || ! blockedPeers.containsKey(peer); + } + public void setAddressBook(AddressBook addrs) { this.addressBook = addrs; } @@ -133,7 +145,6 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter List presences = new ArrayList<>(); List blocks = new ArrayList<>(); - List wants = new ArrayList<>(); int messageSize = 0; Multihash peerM = Multihash.deserialize(source.remotePeerId().getBytes()); @@ -143,6 +154,8 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter recent = Collections.synchronizedMap(new LRUCache<>(1_000)); recentBlocksSent.put(source.remotePeerId(), recent); } + int absentBlocks = 0; + int presentBlocks = 0; if (msg.hasWantlist()) { for (MessageOuterClass.Message.Wantlist.Entry e : msg.getWantlist().getEntriesList()) { Cid c = Cid.cast(e.getBlock().toByteArray()); @@ -165,6 +178,10 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter if (recent.containsKey(w)) continue; // don't re-send this block as we recently sent it to this peer Optional block = store.get(c).join(); + if (block.isEmpty()) + absentBlocks++; + else + presentBlocks++; if (block.isPresent() && authoriser.allowRead(c, block.get(), sourcePeerId, auth.orElse("")).join()) { MessageOuterClass.Message.Block blockP = MessageOuterClass.Message.Block.newBuilder() .setPrefix(ByteString.copyFrom(prefixBytes(c))) @@ -173,8 +190,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter .build(); int blockSize = blockP.getSerializedSize(); if (blockSize + messageSize > maxMessageSize) { - buildAndSendMessages(wants, presences, blocks, source::writeAndFlush); - wants = new ArrayList<>(); + buildAndSendMessages(Collections.emptyList(), presences, blocks, source::writeAndFlush); presences = new ArrayList<>(); blocks = new ArrayList<>(); messageSize = 0; @@ -266,11 +282,15 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter blockHaves.put(w, source.remotePeerId()); } } + if (absentBlocks > 10 && presentBlocks == 0) { + // This peer is sending us lots of irrelevant requests, block them + blockedPeers.put(source.remotePeerId(), true); + } - if (presences.isEmpty() && blocks.isEmpty() && wants.isEmpty()) + if (presences.isEmpty() && blocks.isEmpty()) return; - buildAndSendMessages(wants, presences, blocks, reply -> { + buildAndSendMessages(Collections.emptyList(), presences, blocks, reply -> { sentBytes.inc(reply.getSerializedSize()); source.writeAndFlush(reply); }); diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java index 9c5831a9..dd98000e 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java @@ -45,6 +45,10 @@ protected CompletableFuture onStartInitiator(@NotNull Stream @NotNull @Override protected CompletableFuture onStartResponder(@NotNull Stream stream) { + if (! engine.allowConnection(stream.remotePeerId())) { + stream.close(); + throw new IllegalStateException("Blocked peer " + stream.remotePeerId()); + } BitswapConnection conn = new BitswapConnection(stream, responderSentBytes); engine.addConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); stream.pushHandler(new MessageHandler(engine, responderSentBytes, responderReceivedBytes)); From 7e8c1c758670efe4b54a5dae139b037543b628c2 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Oct 2023 22:27:04 +0000 Subject: [PATCH 096/218] Don't ban peers that send us a block or have we requested --- .../java/org/peergos/protocol/bitswap/BitswapEngine.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index ec2eefce..1a68a90e 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -236,6 +236,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter LOG.fine("Bitswap received " + msg.getWantlist().getEntriesCount() + " wants, " + msg.getPayloadCount() + " blocks and " + msg.getBlockPresencesCount() + " presences from " + sourcePeerId); + boolean receivedWantedBlock = false; for (MessageOuterClass.Message.Block block : msg.getPayloadList()) { byte[] cidPrefix = block.getPrefix().toByteArray(); Optional auth = block.getAuth().isEmpty() ? @@ -256,6 +257,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter Want w = new Want(c, auth); WantResult waiter = localWants.get(w); if (waiter != null) { + receivedWantedBlock = true; if (persistBlocks.containsKey(w)) { store.put(data, codec); persistBlocks.remove(w); @@ -271,6 +273,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter } if (! localWants.isEmpty()) LOG.fine("Remaining: " + localWants.size()); + boolean receivedRequestedHave = false; for (MessageOuterClass.Message.BlockPresence blockPresence : msg.getBlockPresencesList()) { Cid c = Cid.cast(blockPresence.getCid().toByteArray()); Optional auth = blockPresence.getAuth().isEmpty() ? @@ -279,10 +282,11 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter Want w = new Want(c, auth); boolean have = blockPresence.getType().getNumber() == 0; if (have && localWants.containsKey(w)) { + receivedRequestedHave = true; blockHaves.put(w, source.remotePeerId()); } } - if (absentBlocks > 10 && presentBlocks == 0) { + if (absentBlocks > 10 && presentBlocks == 0 && ! receivedRequestedHave && ! receivedWantedBlock) { // This peer is sending us lots of irrelevant requests, block them blockedPeers.put(source.remotePeerId(), true); } From 6e91079fe3a8bd86267f9802fa90689a4bf24174 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 31 Oct 2023 08:41:17 +0000 Subject: [PATCH 097/218] Close aggressive peer streams immediately --- src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 1a68a90e..90539028 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -289,6 +289,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter if (absentBlocks > 10 && presentBlocks == 0 && ! receivedRequestedHave && ! receivedWantedBlock) { // This peer is sending us lots of irrelevant requests, block them blockedPeers.put(source.remotePeerId(), true); + source.close(); } if (presences.isEmpty() && blocks.isEmpty()) From e5bbd268bccff9e405174c534da8ca0e59fc3752 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 31 Oct 2023 10:45:02 +0000 Subject: [PATCH 098/218] Log blocking peers, add bitswap ban test --- src/main/java/org/peergos/HostBuilder.java | 11 +++- .../protocol/bitswap/BitswapProtocol.java | 6 ++- src/test/java/org/peergos/BitswapTest.java | 53 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/HostBuilder.java b/src/main/java/org/peergos/HostBuilder.java index 11af3299..1d4eeab0 100644 --- a/src/main/java/org/peergos/HostBuilder.java +++ b/src/main/java/org/peergos/HostBuilder.java @@ -110,6 +110,15 @@ public static HostBuilder create(int listenPort, RecordStore records, Blockstore blocks, BlockRequestAuthoriser authoriser) { + return create(listenPort, providers, records, blocks, authoriser, false); + } + + public static HostBuilder create(int listenPort, + ProviderStore providers, + RecordStore records, + Blockstore blocks, + BlockRequestAuthoriser authoriser, + boolean blockAggressivePeers) { HostBuilder builder = new HostBuilder() .generateIdentity() .listen(List.of(new MultiAddress("/ip4/0.0.0.0/tcp/" + listenPort))); @@ -121,7 +130,7 @@ public static HostBuilder create(int listenPort, new Ping(), new AutonatProtocol.Binding(), new CircuitHopProtocol.Binding(relayManager, stop), - new Bitswap(new BitswapEngine(blocks, authoriser, Bitswap.MAX_MESSAGE_SIZE)), + new Bitswap(new BitswapEngine(blocks, authoriser, Bitswap.MAX_MESSAGE_SIZE, blockAggressivePeers)), dht)); } diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java index dd98000e..05ddb384 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java @@ -5,11 +5,14 @@ import io.prometheus.client.*; import org.jetbrains.annotations.*; import org.peergos.protocol.bitswap.pb.*; +import org.peergos.util.Logging; import java.util.concurrent.*; +import java.util.logging.*; public class BitswapProtocol extends ProtobufProtocolHandler { + private static final Logger LOG = Logging.LOG(); private static final Counter initiatorReceivedBytes = Counter.build() .name("bitswap_initiator_received_bytes") .help("Total received bytes in bitswap initiator") @@ -47,7 +50,8 @@ protected CompletableFuture onStartInitiator(@NotNull Stream protected CompletableFuture onStartResponder(@NotNull Stream stream) { if (! engine.allowConnection(stream.remotePeerId())) { stream.close(); - throw new IllegalStateException("Blocked peer " + stream.remotePeerId()); + LOG.info("Blocked peer " + stream.remotePeerId()); + return new CompletableFuture<>(); } BitswapConnection conn = new BitswapConnection(stream, responderSentBytes); engine.addConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); diff --git a/src/test/java/org/peergos/BitswapTest.java b/src/test/java/org/peergos/BitswapTest.java index b229af87..22e47e37 100644 --- a/src/test/java/org/peergos/BitswapTest.java +++ b/src/test/java/org/peergos/BitswapTest.java @@ -1,6 +1,7 @@ package org.peergos; import io.ipfs.cid.*; +import io.ipfs.multihash.Multihash; import io.libp2p.core.*; import io.libp2p.core.multiformats.*; import org.junit.*; @@ -14,6 +15,7 @@ import java.util.stream.*; public class BitswapTest { + private static final Random rnd = new Random(28); @Test public void getBlock() { @@ -43,4 +45,55 @@ public void getBlock() { node2.stop(); } } + + @Test + public void blockFlooder() { + HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + Host flooder = builder1.build(); + RamBlockstore blockstore2 = new RamBlockstore(); + HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, b, p, a) -> CompletableFuture.completedFuture(true), true); + Host node2 = builder2.build(); + flooder.start().join(); + node2.start().join(); + try { + Multiaddr address2 = node2.listenAddresses().get(0); + byte[] blockData = "G'day from Java bitswap!".getBytes(StandardCharsets.UTF_8); + Cid hash = blockstore2.put(blockData, Cid.Codec.Raw).join(); + Bitswap bitswap1 = builder1.getBitswap().get(); + flooder.getAddressBook().addAddrs(address2.getPeerId(), 0, address2).join(); + + // flood with irrelevant requests + List> fails = bitswap1.get(random(20), flooder, Set.of(address2.getPeerId()), false); + + // now try and get block + try { + List receivedBlockAgain = bitswap1.get(List.of(new Want(hash)), flooder, Set.of(address2.getPeerId()), false) + .stream() + .map(f -> f.orTimeout(1, TimeUnit.SECONDS).join()) + .collect(Collectors.toList()); + if (Arrays.equals(receivedBlockAgain.get(0).block, blockData)) + throw new IllegalStateException("Received block!"); + } catch (CompletionException t) { + if (! (t.getCause() instanceof TimeoutException)) + throw t; + } + } finally { + flooder.stop(); + node2.stop(); + } + } + + private List random(int n) { + return IntStream.range(0, n) + .mapToObj(i -> new Want(new Cid(1, Cid.Codec.Raw, Multihash.Type.sha2_256, randomBytes(32)))) + .collect(Collectors.toList()); + } + + private byte[] randomBytes(int n) { + byte[] bytes = new byte[n]; + rnd.nextBytes(bytes); + return bytes; + } } From 9d8843525cf79897e1e5ba950f3378c546000bba Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 31 Oct 2023 21:18:31 +0000 Subject: [PATCH 099/218] Don't log on blocked bitswap connections --- .../java/org/peergos/protocol/bitswap/BitswapProtocol.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java index 05ddb384..bfc9da43 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java @@ -29,6 +29,10 @@ public class BitswapProtocol extends ProtobufProtocolHandler .name("bitswap_responder_sent_bytes") .help("Total sent bytes in bitswap responder") .register(); + private static final Counter blockedConnections = Counter.build() + .name("bitswap_blocked_connections") + .help("Total connection attempts blocked in bitswap") + .register(); private final BitswapEngine engine; public BitswapProtocol(BitswapEngine engine) { @@ -50,7 +54,7 @@ protected CompletableFuture onStartInitiator(@NotNull Stream protected CompletableFuture onStartResponder(@NotNull Stream stream) { if (! engine.allowConnection(stream.remotePeerId())) { stream.close(); - LOG.info("Blocked peer " + stream.remotePeerId()); + blockedConnections.inc(); return new CompletableFuture<>(); } BitswapConnection conn = new BitswapConnection(stream, responderSentBytes); From f670f05f3670d04220a50f4a45163419ba661540 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 3 Nov 2023 08:45:15 +0000 Subject: [PATCH 100/218] Add more kademlia metrics to show outbound traffic per api call --- .../peergos/protocol/dht/KademliaEngine.java | 35 ++++++++++++++++--- .../protocol/dht/KademliaProtocol.java | 19 +++------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 5a3470d0..a2139d60 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -20,6 +20,27 @@ public class KademliaEngine { + private static final Counter responderReceivedBytes = Counter.build() + .name("kademlia_responder_received_bytes") + .help("Total received bytes in kademlia protocol responder") + .register(); + private static final Counter responderSentBytes = Counter.build() + .name("kademlia_responder_sent_bytes") + .help("Total sent bytes in kademlia protocol responder") + .register(); + private static final Counter responderIpnsSentBytes = Counter.build() + .name("kademlia_responder_ipns_sent_bytes") + .help("Total sent bytes in kademlia ipns protocol responder") + .register(); + private static final Counter responderProvidersSentBytes = Counter.build() + .name("kademlia_responder_providers_sent_bytes") + .help("Total sent bytes in kademlia getProviders protocol responder") + .register(); + private static final Counter responderFindNodeSentBytes = Counter.build() + .name("kademlia_responder_find_node_sent_bytes") + .help("Total sent bytes in kademlia findNode protocol responder") + .register(); + private final ProviderStore providersStore; private final RecordStore ipnsStore; public final Router router; @@ -68,14 +89,15 @@ public List getKClosestPeers(byte[] key) { .collect(Collectors.toList()); } - public void receiveRequest(Dht.Message msg, PeerId source, Stream stream, Counter sentBytes) { + public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { + responderReceivedBytes.inc(msg.getSerializedSize()); switch (msg.getType()) { case PUT_VALUE: { Optional mapping = IPNS.validateIpnsEntry(msg); if (mapping.isPresent()) { ipnsStore.put(mapping.get().publisher, mapping.get().value); stream.writeAndFlush(msg); - sentBytes.inc(msg.getSerializedSize()); + responderSentBytes.inc(msg.getSerializedSize()); } break; } @@ -94,7 +116,8 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream, Counte .collect(Collectors.toList())); Dht.Message reply = builder.build(); stream.writeAndFlush(reply); - sentBytes.inc(reply.getSerializedSize()); + responderSentBytes.inc(reply.getSerializedSize()); + responderIpnsSentBytes.inc(reply.getSerializedSize()); break; } case ADD_PROVIDER: { @@ -123,7 +146,8 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream, Counte .collect(Collectors.toList())); Dht.Message reply = builder.build(); stream.writeAndFlush(reply); - sentBytes.inc(reply.getSerializedSize()); + responderSentBytes.inc(reply.getSerializedSize()); + responderProvidersSentBytes.inc(reply.getSerializedSize()); break; } case FIND_NODE: { @@ -134,7 +158,8 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream, Counte .collect(Collectors.toList())); Dht.Message reply = builder.build(); stream.writeAndFlush(reply); - sentBytes.inc(reply.getSerializedSize()); + responderSentBytes.inc(reply.getSerializedSize()); + responderFindNodeSentBytes.inc(reply.getSerializedSize()); break; } case PING: {break;} // Not used any more diff --git a/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java b/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java index 49429bd6..d087e5ae 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java @@ -18,14 +18,7 @@ public class KademliaProtocol extends ProtobufProtocolHandler onStartInitiator(@NotNull Stream @Override protected CompletableFuture onStartResponder(@NotNull Stream stream) { engine.addIncomingConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); - IncomingRequestHandler handler = new IncomingRequestHandler(engine, responderSentBytes, responderReceivedBytes); + IncomingRequestHandler handler = new IncomingRequestHandler(engine); stream.pushHandler(handler); return CompletableFuture.completedFuture(handler); } @@ -98,18 +91,14 @@ public void onException(@Nullable Throwable cause) { class IncomingRequestHandler implements ProtocolMessageHandler, KademliaController { private final KademliaEngine engine; - private final Counter sentBytes, receivedBytes; - public IncomingRequestHandler(KademliaEngine engine, Counter sentBytes, Counter receivedBytes) { + public IncomingRequestHandler(KademliaEngine engine) { this.engine = engine; - this.sentBytes = sentBytes; - this.receivedBytes = receivedBytes; } @Override public void onMessage(@NotNull Stream stream, Dht.Message msg) { - receivedBytes.inc(msg.getSerializedSize()); - engine.receiveRequest(msg, stream.remotePeerId(), stream, sentBytes); + engine.receiveRequest(msg, stream.remotePeerId(), stream); } @Override From 099399797f6cc3080729d01de544f8a0af5de3ad Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 4 Nov 2023 19:51:04 +0000 Subject: [PATCH 101/218] Don't tell peers about themselves in kademlia Add test about find peer responses --- .../peergos/protocol/dht/KademliaEngine.java | 9 +++++--- .../protocol/dht/KademliaProtocol.java | 4 ++-- src/test/java/org/peergos/KademliaTest.java | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index a2139d60..d4596998 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -60,11 +60,11 @@ public void setAddressBook(AddressBook addrs) { this.addressBook = addrs; } - public synchronized void addOutgoingConnection(PeerId peer, Multiaddr addr) { + public synchronized void addOutgoingConnection(PeerId peer) { router.touch(Instant.now(), new Node(Id.create(Hash.sha256(peer.getBytes()), 256), peer.toString())); } - public synchronized void addIncomingConnection(PeerId peer, Multiaddr addr) { + public synchronized void addIncomingConnection(PeerId peer) { router.touch(Instant.now(), new Node(Id.create(Hash.sha256(peer.getBytes()), 256), peer.toString())); } @@ -152,8 +152,11 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { } case FIND_NODE: { Dht.Message.Builder builder = msg.toBuilder(); - builder = builder.addAllCloserPeers(getKClosestPeers(msg.getKey().toByteArray()) + Multihash sourcePeer = Multihash.deserialize(source.getBytes()); + byte[] target = msg.getKey().toByteArray(); + builder = builder.addAllCloserPeers(getKClosestPeers(target) .stream() + .filter(p -> ! p.peerId.equals(sourcePeer)) // don't tell a peer about themselves .map(PeerAddresses::toProtobuf) .collect(Collectors.toList())); Dht.Message reply = builder.build(); diff --git a/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java b/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java index d087e5ae..cb961ee9 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaProtocol.java @@ -31,7 +31,7 @@ public KademliaProtocol(KademliaEngine engine) { @NotNull @Override protected CompletableFuture onStartInitiator(@NotNull Stream stream) { - engine.addOutgoingConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); + engine.addOutgoingConnection(stream.remotePeerId()); ReplyHandler handler = new ReplyHandler(stream, initiatorSentBytes, initiatorReceivedBytes); stream.pushHandler(handler); return CompletableFuture.completedFuture(handler); @@ -40,7 +40,7 @@ protected CompletableFuture onStartInitiator(@NotNull Stream @NotNull @Override protected CompletableFuture onStartResponder(@NotNull Stream stream) { - engine.addIncomingConnection(stream.remotePeerId(), stream.getConnection().remoteAddress()); + engine.addIncomingConnection(stream.remotePeerId()); IncomingRequestHandler handler = new IncomingRequestHandler(engine); stream.pushHandler(handler); return CompletableFuture.completedFuture(handler); diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 07d93720..307ef707 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -4,6 +4,7 @@ import io.ipfs.multihash.Multihash; import io.libp2p.core.*; import io.libp2p.core.crypto.*; +import io.libp2p.core.multiformats.*; import io.libp2p.crypto.keys.*; import org.junit.*; import org.peergos.blockstore.*; @@ -122,4 +123,25 @@ public void ipnsBenchmark() throws Exception { node2.stop(); } } + + @Test + public void kademliaFindNodeLimitTest() { + PeerId us = new HostBuilder().generateIdentity().getPeerId(); + KademliaEngine kad = new KademliaEngine(Multihash.fromBase58(us.toBase58()), + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore()); + RamAddressBook addrs = new RamAddressBook(); + kad.setAddressBook(addrs); + for (int i=0; i < 1000; i++) { + PeerId peer = new HostBuilder().generateIdentity().getPeerId(); + for (int j=0; j < 100; j++) { + kad.addIncomingConnection(peer); + addrs.addAddrs(peer, 0, new Multiaddr[]{new Multiaddr("/ip4/127.0.0.1/tcp/4001/p2p/" + peer.toBase58())}); + } + } + List closest = kad.getKClosestPeers(new byte[32]); + Assert.assertTrue(closest.size() <= 20); + for (PeerAddresses addr : closest) { + Assert.assertTrue(addr.addresses.size() == 1); + } + } } From 9241b6443360a9979ae5ec884be599d0a3483373 Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 5 Nov 2023 19:16:05 +0000 Subject: [PATCH 102/218] add logo assets --- assets/nabu-logo.drawio | 98 ++++++++++++++++++++++++++++++++++++++++ assets/nabu-logo.png | Bin 0 -> 28027 bytes assets/nabu-logo.svg | 4 ++ 3 files changed, 102 insertions(+) create mode 100644 assets/nabu-logo.drawio create mode 100644 assets/nabu-logo.png create mode 100644 assets/nabu-logo.svg diff --git a/assets/nabu-logo.drawio b/assets/nabu-logo.drawio new file mode 100644 index 00000000..ef6709d0 --- /dev/null +++ b/assets/nabu-logo.drawio @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/nabu-logo.png b/assets/nabu-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2fdc703daa6401cbcef7af10ce3c840d649feb8c GIT binary patch literal 28027 zcmeFY1z4TSk}iyE2p)n2cXx*bCpbZaui);kL4pMc5Fog_OK^7x?#{x3!@?ohUD<&> z`|O!H^52>H=gghwS;*?{ue!Uc-nXjd3s#VmKtaMof`EWPc`YgW2K=`X{8Nnp2mY-; zwmAp^K?iaWQ+Kd*F*3CaR9VhIaO*)>aQQU}a=xvp~tz?O{YLkIfuS4Girc9)6gjwY7zV zsm)&&>RVe`8R|b+=|LjE&d%ETFPj-zTRcqfp^XiA=6@mmXMvJFz~Wz*s+k%%nEYzX z&C2y_EI(UI8k!oLJoXHp@~;|8z+>Zwnb?~E46L1hJ^x$LAIa*qQ$EXMfj*-S4jce}`G-*~rx8 zCw_(P9qg>l4IeGT!U-<2vv#yH_zm1YpZL{4#M;8z?l;x5vFUU0aD#Cm45kf>$8sxc zD{zNk82sw1#`zZ$Nf=sN8amjyg2g^a;ob=xE{Vc z|BHLEGe4A>{K2(;F9bZewDIq!oe?j6;yn&mFgCPfQ`DgS| zu(Gj0@h`NYU~LM{IDY30)<1a|E8D}69(%I4cC^zse0b{bV}rlZB|DfSe{ zW0wv9J7YtK|BEicegpW)i;n_re$LtcXQzL^uKrz(vHeM7ERP2J+cfz{#r(VS*?Im_ zJ*@xVM< zw%Q|p|7!@H>yf}8*p>5fx%22b|M7&*^QSJk9~tto%m3bl&h?0`2aRz*rp^CBgwFjZ zLg#q|_&+B9pN-JLZ1t0{{u({}4cMfBs)|KGOg?>W{3ME{w$MOgoir0ahGtFrznWd5GS`~?Vq1(-i#@V{Z2 z{4)sttvL9o5YGW%oN`lzwT%;z6VV}HgZ z@Gc#A8~9Hb{`26=^SG(}(B(fF zeE&>%zaN7BkB0B#n((jfpZza<|I6Xa^9a|+Sn{}X{Lh2$pU;Tc{=6Re@12};{V8Ah zXN|tUW$*va6~^D{{^GXiA8#i7R{MBA^;gBi13&Nh{<;_9{HAsZ0)iOgwWyG?i_X3_ zd=i$}c}c+mVxsyh#gBS0v1!CqTgi~)G)6XRZN2Z`DzfG|hp&hNpfgf(Q*r~T_nzb> z8DAFWF0J4S%}8Rwvb@LOl-*LeFLiSU%xuqY$0yImYv*{kL^{?qq^{{Wl1_ffWRo5$HGdg1%V~9ng=Lb@ImC0o zUOn8lmqp^4_P$Z}< zmD&^hH2#_<8^vX|2GWKJo}l`Qjd5Qyack3SL5V|dZ7%F`Df1}GaAo<)&A0O!p4L!? zsz#hv^Hz&SqLC|ox{WjQ;-co*R*lvCdLR3JMUOtkOM?|M|0F>Ouf~I~yd-oEC5tV# zapz*}bKM0HT?J;j=hx|rLv3n=l878Jcce+bov(!#|AgOnw#74SG+@i;%AW^<}|;QQ8`x8jJ8tCGOI8X$9H3s(@K? zfSqH%ADvXV%*;_j=ZQ}ff7tH3r*13bjn=b}F7>yN`zdeLQ0H*v5Q?F^mZCy}>xXjJ zRz(uN@~SFC45^pP?I#kTEYyfZLL1m0`g{}PW0(m59WPpK+t}6K>b2>J6H{!rod@)w-;&Z0Ic3g~@*D8@?1R}T{=NPNb9NQ5r)+NhrCLuTQO85{Wr6;kd z+DvbG;m=pZV<(Q6_%{||?rZUfDEVkpUm|^?k=`$)9EhWZA=VI_K1<2|gwD=PbWh4Ovt)F&rX zMNp&lUYXEq7V$S2e<#u&f7VdPfJx8!wPnM#(xE_}LsfjpTh)Nop?eFnTHJm4o%5`* z0%JQJnTVRep?-gnJJJV>h;zZ~`KtiwLU$I~+vjV7S|5|`oFt5L4g(VXv|>nnn9h|2 zp5n|Cm9ZOyW-Cwb;S4?>zX&w(SF5dcgVNu*tp$4p3H*WP)3oq<_r2EC;)RRyF1kSJ z2`40vjGnE}7s4aE2ry0c@3t!-fuzS278)?GTv5C(u{6$KexAJ-V0d|GyeBeesY2_O zee;Ag@(uq;TX%(2@0RRKYtQT8V6t1)Zkiz)jVd?hm>K-bBLx;?8RO~QmlH0O@`97* zXN1@LuFRH5i`b>_T$>%^% zU%k(poZuJKzd59r0Bkbm*NTr_@oO=@flG6@@XXPgwD!AXncAF7Euo_+re6sP)Ek&p z@VSp>=j2r{|6u0gd=>X;Uk}~Dx7uTZu=k6=QNmirTv0dD<+}?i+$5*IK<8CTpYJ9| zBdc8BN|J!$V`N9Cn*jJEGtkmK(YGCyEHAhA;;(UfBth*h+|PoP<3CL2dE&)%n`pQhp)!EDPLvwh5|>Q#>y$h{g#v za_67Jro)54cB7R-FRg`%jm6|#=O0VsXEt2`YO5Y9R@ClU4dnrzwmV!4lkJV!(4|kh zcc1WgK|ew7jkf5%0)_rSB5Kp5k4B`Ri!}^BurCzrl9>sKoVHHdHs9vE@-}}UE zNRa2;Ljat+Fx=T;yy+J>wVy~2^aETRwx++dBJMiF3WiaTnw@hbV}rV~Rce^U34vON zkhA&#P7LEPXs8R4J{xHtv$%m?A}oxd8rBy_yi^pB%X_+&Ax_C4)G|n|4;aI$2e~@BbE}KE2B!WYeh(pD` zDP&1Ns^j*%VZ6YwsatVosy&ihiRc%wXxKcWaCTCs~zu9pq+c%b6+|Oy?y3*Oe4wli6OqIC^F#EM^xM-+kOM>8i7sp!+iTtABYVMDvW&9qE&Pk92~BD=W18?t9KV zuG3FTr|9>!2DvP`lt7v*!pPLfrc+3jecafgVQ1-%5^k?&$7Xo;Q-e>XQcmD|KdN)6 z7(?9X9~4Mih+ZRi>Ss}Ng(6at_~Lw@3;^Y+TvxT$v2{NQrj6lO-M~JmpMc>fF%7=_;yU(eW!cF6wRJ`Pc<(<0(eB-Bx!A);`{K;sR z9M9F~#ilO)88;fQUU1*;w2{W}m`a2|HJ^E0ib@xnyxs^?o`x;|{I*%}d(6@%(Bf_% z_I5YJGdBF`C5HUjVp5@ZXKmQ(5%5R?;d#i+*#?J<8((~OkZEfovD4JcDFT}`rK=~n zVLe}Fk0o_4g-O_RsuseZTwWo2?`QCO{XjTF@FR}<6l7+es7hiqG@ClX>$>#uC^+}U zo*t#F9Y-UCE5LNNGAQF?SA_4Nd8!|4U+K1iB)yTW1f0iFREVYm^_4N(J|m33?;S&Z z$>yv5O+1I^3>)GU5wh=UmRJgwQ=;lk)OxE^Ql~%wa_$~&$7azgZ@uG$kjaY_>K#3m zl4a`1A8M2m+tG)4J}oN&sL6hnZa46!!6L+n%J<$xw#K}6#LkkrdLnEvV5oB z$@&~a(`KZhcwf>@^dSmM<4EDmGaE*93c`?l=BcpbUyd<~ql(s3{A zCXeC{NJ5&)E?4Zs_AWbKr|j=@$>W;lPFFa!R)lkUXmXKW-y)q*rq&fSiQbkh7a|b} zt|e?C=DqvU{z-ZauNj->dqbTAt@a7-%}^Qzi(^Ls+LdU+=Ww`7Qu!`12tK=yuLun@ z7I8zqj#>bH`H+8jg|^_AxM116^i4|O1L?ELI+m%oQVuco7pvTP+G7=)koHaPERXv{ z5a&lxs0^pAbZ~GpbP5E|YNw-bubP;!^5OxJ1q`j<8?IPD=zxX;2`=ZW7C+x&Q}(A2 z6KEVJpNZP&0`z6iowq*0#6W$p9#Z%1;OL>G57X&q z_p~;@w+QFB&D~ef_2Y^L;4s?yWENI*bCDiTyt*Yk3{Ih4UmSlQ(E*=@EGg0iWPk#$)P zgU{tAuK@g7EJtSIP?a$)xsXIcQW+q=8gEAqPJr(fy}@ev!ZJ?YqQEjOiJ1iC4ZkzR z-1b(S{yU zK=AwU!Ux;ez&N(BWmR!4Co{y<-h3tsvgKD#T$)a~!54o*YUasu1*tK@;_J%|aTu{HHFF{XUST<{TqNj(S6t~H91tE& zvO@&Tl^y^`caC?`&`VIj&{$<{Xe*F_EW&w{{}seXL1*k!iHO2pi2^9Ep{Sj;FVDC& zbL=2T+>xBK5wm!6(<&n;GAyVX3(={4NUP>*6=WcaULn*vQz66!4_m z9R}YvLcKQNB#%@aCue0xYOH-<>0CTvmIDP8lfag;KZN60a{00(0j0Mu|C*5YB23CB zLek+4*fP%`bocI_n!+}ya@1|hDDyYGAHl1M*~$G3(U^No<8zB-at=KxbwnRTewV9o zj;K_-F%(uV_zL_WxpCRki^5Ah;FhJ?SXx`z!P>C;imVJLFcgQZCv{Z#qdt)wF7i9c zEgI^?g#)FHXydNnSpJ2M97tjgg?+HUqRd1rnsCP6McBnig-|4+}U%N`x1B zw$Br#grb=Ezb7$v=Qu&~bH6mlTZBywT^v$>x}kydw!%@d&W#vm9FuV%1tZ$ctlle6 z##C1eQPl|9-LKmTROw`Y7Y7`W4jtFOsse*01Zi~Yo^;MkV--$@A5)=^p3cIc532Fo z;J&ux73pQaMEmX7LR7cIDZ}EYJT);VAHj^AyXoP+?`+Vrh`Lx^ghlg1nX?eyj7sjj z@ozMl<{R881`GRGZ6!hSNa8Cx%2C&lr_gO%PS3EqkMdkO7|7cyE#dGxXU9wNY7tRm zKQ&jHwraxOMD=@n+$HQ~%LcuSL|aeH{LE^oK4%z0y$Rs2G7OH#9I_$lLJ6wKr#W zWo9+;MtT}a1*-C3ugJh6a#o{nwey6PLw%hi>|sPReN!Xzf{*>3#BmlRA!_)b^9Sff zc|3;=sQ3y#y;xC;YqC!0%5|?n08_BU@+8Iuo~X{`tOCJeS13i&tzIzPqAXbrziPB4 zQf`ZN-VmL<=#CB85g>K<@?G%EwltDbu-ySF4om7(Ore-K32D$7kmMCa z{+`8IQzkr=-RtOrCzP% zrhA~VGMp{GtwGCv*Ut$HS)e9~fnb7DT+RQ2gyPNQg{3_t@|2lkdC>F6a#5`oLr7H6 zoyP3qo)vY7y^U;disS;kHg1bPC#RjU>r3+3^a^^ppTc2zN~Imk9T4SXd%i#!-`w&=YRzb}>J}z1h7c*k^`J^KOpLWkaW0WAF$t*fs zbG1(_93Cu*>F49>VL>Xea`XP1&j$GpaePC@yqw+Ia#F`e(}=t>I~f zAyB_L#|-1tjz~cVDnohr(<_v_H)e9OfrK0Pw~QhiKIO+JTDn8yZvrm0pD#By$8S4K zP+4fdrQd(8DQEqgC#;sZJ;#E@l!cU+#ExFKrwSViohc`efzYj}GJ7^%g}cIDB$d^M zGD%nquWehIt{LK9(y&L5AEWyL*1pVCNgsVHd)=G)tVf1%3llpccXk5I7U-a^gMN6u zp@lGcNof7rY0ln+eHps<@t>h?q6(&8u{yq=?M6qq7e@w-VmEC{=7#d|r@!o?-|pq+9dV6t!G{;4J3yNmZN9(#B! z!e6N~IFl)55@kvj8I)L&xmZ0Z-@7VYt0-pZ*s9&7on>H3SLr6eHHrmeHxzzPBYo*# zpP_j2skAl51t4K3EqIWx9t5?c=;9GQ#lELj@>+z7f}5);dn;9V?tPqjM$MF=(eR{; zV3bwfDf&wFuKsN2Zw3IW(?It0!;FVv8|tR<`DT}R(E!HM(hPO(caRL|NSOrL)n1r3 z9x@-iD(JLDSE8T~89p{eZBQeMeQhBP^p;3nU~bAP3pP}~i=&;jPUhTEA9=6-a?GcO zx=>iYOQ{#Bg)ld8eJqV~-CKDhvv@r*3mnIFw4I@pZmj|IfnUUV5Zx7QdjpPo!>g#5 zfaBD>p7(GBB!pNg2!irT=P;4h(9zv%C;ia}bxUq)tzgC>@p9R zINNSH$^_1^?CvNQeQNdVGm+4z^oz<~CKNvXu?hL3a-g5?LWgXzlshX(`2ZXFnB}7W?#WF+NHoMfWsu^ILgG_52lOMB8eyU>MJ*{F)B3aDk zpi?RTl#m4x$NR9Y1TXDmrfE#s;jvY<{^3_7CNybgr}9woYjyZeuy^_#XeYG|X7qXc zBYCZjm%`&$U)&DEr?uC_FN>P}g9TBCJ;)`^?;+jqlzo;yRy000^y`W~k-sFtFvsPM zruWg2$fpeGBX26LW)>5Hc-f=vRM@$lTHfgpD%OFpGNAoNvDM-BX(O|d_7Z4dEY9RB ztG!*6n`?qBG5byN^Aqa?PDRQ4Pj*N5BN+T~+c*1ex*gh)A+?)zg^WT_cp7c&jJaAh zXYqC_EhwuJg=uy6cbef#0;rmSK8j3hgh@-;*Jqo>2{9C6f$|$7^ie7sZxd=V+}p6K|;7Jj*B*)UXCC_T2Qva*C%SMcBQmdyt!XIwDs}^UwFB z3}SVvqS?SZOR?*pU(|;T70VGt?_vk@fhs8>>Ab8Le|Ybk?7NCF7^_JwY*`sQbYd3I z<+q;{*RpB~(y?;k=&>1NeP(Mf;Lxu3=#TN_5*@P|ys)iXJSWU*@vMpCVXdfp5%Y}4 z#q^}ij!0vzRP*_umN;FH`Y=xX=Lq+y7p1lv6wm$q#02Qi@gwpJc)hz-{Jg7J&!fgd zUfT3;uHt@dA`3YlUMf?1OW4T!0YZm8MIhkZ9lgM?3XR)A31tVB$=VfA1if1{9YYV< z(^w+cec9PB320sJeR~l{g+s6s<}^T&y|JlRfwRgBnDUv65_rAy8J;o>zmaavog-QP zNOoKSjM(V7J6C(AKuX0m02kAf_w$}OoyK20X0J8gMo z8Zh^_w!Sjb)JZav@@?QtByAm z!zwV7ZhA8+#*;8S(M7fyIw?4%wW62;onP7XYjPSVh>e;|5i~!K9{)0j%oLDuq}jBT zls&o!&zoU_5+MD7Ky26CJKkW-vC~4Pdq?V$)Fo$#W8Kii*-i21(Gz(q2W;a}cG11X z^Ypsqg2sfub|!WfztwyXmwb?AErj2S#>S(KdAfxPtDm}a})Z>M6zRt?<(tawZ|hON{2Vy<43jxWEzEXW#V#Ed@PG$cTk> zOK`=*^2&XSVyFJ*Mq*KSlQ-{>hFP40iuej(Y#E!s&%}r5Ef{&@gNXhv$$GA{#g56x zB}S++OgjUOvyqlfC5GQTAhz_YME!O=cJ-QFa%Q=o3sh+5dvz)WTv0DC`FD?Z0i5-o zF0E8}EKexjR;$B`B?YFi)+8S4xCclhkDO*GIK0AW>~1f(JwXI&_8xrx0pmZAyU2nxP+RooDY zXLUytmdJCHe-}ffyY6Q`3ZK@2-O(KQVcsmu$!QEar%Ue%113|`3*WCIjXZ(y`xBBY5}=yOw0~NlDiVd?(2ko4^xDv)w1;RfV#|+#8s#Go)I0Q`=pTS z`>U$if!6K`#87xhVOn2*xDO<;HGmrV;@!xaB@W*YgGbgcK(b?rw|1^vl#!9@I>vyg z^cCST-XqvvDlBrdzx^LW7%eHg&5w6Kk@p*8qwSJ^HNj6PB5 zeDB5?hw@3YLvNNMwO*6P^~8H|jKYy0 zxwq`i;K*{2Rx9L1dQw`e)(LvKFRfLWT#rX!=Zi@xBT0&2a=>zq-@PdP#!scz&|-%OK1HRrD%@*w=k`i?aq@FvZI?9D{MQI{sjy5W-@r9GSi z5wAL_1w_=B`h6CX1dO^gz_O(1+(8Pvfp7`R&=V{>X1l|>X2K1DR5GRUs1MRPH*+_O zp9y`1F2qiFMz8p0K61X9<|tUKvdz7FZ4P@_4AqRdm))B{fG}u9etr@XXdJ+iYOJe^fXIX?C5=I5#vqqAbBkUMi{Y_BB{0u)=mq$;DedWOB z74sM%{|>8pja24y+CFntJ+%$7jMswrX)y}kg}G#zE!?Nz z45lmkEquk$kuO40y-opJ`prw(UIb510gz0sR-4Bs+$&g;Gh4%CQM&-HMd@8AY}Ie2 zyc-01<8O{vceGMSlGEAl&^Rk@MKR*k-=*W+t)S+y&1pc8u1~2`6$Y1(`-aP%I^7_C ztw5ct)1NKf^;(FfrFq)-Wvx4j@j5b!zV1`K7h+1Aq=A3VJ#SipnwR0V+t=AXZbHTBpGkCyvtvT{jxAoTz zczHL|)ye^bN1r#p`DZwMLT*>gq{h|ooU)Ro>+mdh4q}KSPdLiHsZ-#OXsoVoP*OZm zOa&S9TRW$3J4y+j7gn{}1?vXq?m(?;cUVzn1kLB6`){*48hI@_AE_58>3kaHC-&F5 z+~Jbw*SNjkP9mB%nLjD6ml{s~xof7rK50@@xK%3E$|Pdg@iEK60EH^GhF&WCyCUCi zo$&nT4^~2mDeGsHIpt%%!?~2>Eop{{1q#ypg8(aM|54Gt*|x38(p;yIgv2M~#%kC;F!7P~>w-Q)LPUy7Z;L zJ(alrHvJ0MHiMZ7HWf8)hKHe{+8?4hcv&A8&aQUkKx?yRlJzRPntpi2pLMzEPFPks zU$HvZGNb)$33QpZ{EQC=vI7&H7SrJ9E+ol=#8$p$&QLT#6T4L|0(Z9Lo8Bl1K5wheNj*fqgJ?9x{8J;_*pui>ghYSG=^rJ}}Ga2qW3 zM$KaA2~S6pF(%3994W#hk9KH-&&~nJCG4Hua3j1QRTQJYouhGz(2?@!|FN7r( z_8h}}?QC?*Pzv^H=a7ay+1J9qFSL(UsG(8wy~s&0&3reZ|FUF4QV(%SI>PwuPMl_) zB=8pg`@m~BFX~(5m`Qgk?dG`F{wE;!K57~fq7J6iQj?%P97Gz421bIL)wrF}@rL9c_CbC6$(TbPG&6q)QP1a&_}P>8oJk;Yceh)t*z<8vz9 z^h^H1;WJd_T$B#VyHO}^$zfJKDelz1g1UJNs}P=J&`RPXaZ$qsoUkyk5!MtjTfRUk z=Vhkbtz2|lth&R06H^rgM&T0n7Ut(Ik|C+s~ z%8oI^?uYZoZxQ=L$4z5)IMy3*#mP#0jG7@36OwIg@}rs`*+qL0aR*RQ`3zt$2bsc+ zh#+1GyvMdA(0N{|qoPaeuY)moJ4YK4J55CZ5^`8;FIo?AyO}5qw6O9DSNQE-$b2sN zUI-78+P)c?p_Vz6GzKu>jNC=rsZGb>8hTFP2mym5f^C+;8m;IDvZQut$B91#q4*kA zl$3Z^y9ogkkO~iEY34d^3fba(zKTJP1SeAQbKleJS)aczyR?PI1rJ)lO{x3|DKXp4 zrVAwZ+;tXYglr+RX#%E@1|tugrv_QaR7!{5Ea{$PCsI9f;8QJU9-G6~C&>GR0D!Pl z+e-&`PtT#kqh4!yP9QpoHzB$mNc#g6Va1PEF;`zic}VDc6S5$e@qqbNk>)o@VH|sz zk?dXDo17{`%9ixR?)kF)ISIqYRB}|2%8tx#y%NANG^4B^a=iTg9s2fn zh6$wdWn*C@wBTzKOemKyP|dEaqIQb0^1N@yvIQWMKcOT`a=#n<)R%cuFpm)_%L^1x zA^gIQ19+)FcJY~EQe~MTvL0O=dDUb{9=iDj1QVsG4eR-S3JcN4CEsa*PD838MVf}Y zoN&bujyQ~cjy_-OMw!Z@Mc@+ zo(5GL0#PU~{!{nC{0S#%D9yRdGhCJi7yUI!~~+D~U1)4r?x1$OXQ#nOq9XU!73l374f$C_I%#XQlo=Py(1J)yUN2uf$HX?jUEmr0yF(Fr=;_ez305gL?0aZ81#%i zxj;NP2a$Q2I>AkjJs-~!vS#+HX)6^1D6BJc7RRvL(K$0bNce5*lP7Gt6@5tpJcZqbX)T5(y3UpKPxwHej43VNea zE?39;LlwU=X_X2HzY23uj2T}OYB+WTV--VkvUQx`q5#v75f#2m%G&_?sYpQ~@qC}~ zk|gJz3c5Jzw`fz&I^jy_y^G1%3)mT0XK9ac185xwIDqKIE65;qk=}HeOB%Yp-R6%U z=%@svOdF-=vP{J~I;&Txg+BS8Ft@sKLnKe|vB&aEZ}bZmt^@j!6sN|~=QGcw9y{1~OX z4#n_ZDdX{fROQ-gZX2vXTA)V+0A)FWn~c=oqLPY`@N}5t0th~xaUK!SR|c)+8~h$EbD#uEEeaQ6&* zMu>KNFoNw>9exen%)x|$rA!99tn+bogV~@Ew`KmDL|?)XEOc8O#8u!{%>`lZ8$%Jt z$)|CJuh?76;>fTehz3fZL%C4izNm=jtV_EOcS4s8WqJ)Q*!uyeqTDPmaI-v}SYE7! zLh74M={>;jS_dUk(6UrkLkeO^=?tMl_|S)21fzH4^M05c#`9GA@4oCnNSE5@qxMyb z#+tAmF+i{fqIL3EBA;_ZEh_LnGZtBp)UXWn0?F%Qq%2l|al|)wA*4 zslyw3T!xQhKGuV%MDmju9;M>vK;2fFD|La$l(5`TMr?@t(?WdIV_FnCX7N!-=Ji*B zACHoz7^OZi38Gx8HnIn|yN`G}x9PJ~a?*j`G`g%RL4tw^EB6`|XUd|}WsCjJ(J9Q_ zHuIEaL(ExGE-71)o%IW~UyXbxbQreDhRhIxr^E`@PydXi*htS;raiR$bN??QoH!rC zh2>n>`HD@luNKbA2W`%xlPcDucp3ZIq6AmxNo1@&cvjNDkVq=Y$pr%9S)mlcvqWXNO%hfJJlVp*w zDINw1RFaZO_Om?$Wx;GnB7L_qTz^>CoxWy4Ia)q8Y7_(JZh2vmWz8d~w8qj&l~#F& z?SAWOORwDG#2IBskG}T)R;l`G-O=M52(-nnSJMqeaYghpRu#@~ zr{l~hsrQ6d0EZ$npom6vph?r#bwRa2KmnnT`^GS1vu_35TkNjHl5 zg{~gmm&=pgBJP@X;Pcge1Q!+(ki0wd3C;@U7)d6ZjWnDe#FB*S4uw@2%|mg$-a3p=-G2Ia1-M~zwM$`;J6qNqK#AKT>bymAyZlX#zjm1>(yx(GM z7vd&8$7-zVLj?G$WF>I3_S;a6y;H7SADT>sBo*@{_28WLxar*~9lV8^`K&JZ8c%pb zL2l>dg~Ivtu-3DQo;r5jbQHwC=wDY3E9zUM@;x{qh@y5z_^nN&#m|!QW$_9j9Y}T4 zG57hu(7;Q)W5AqnM_N&v#5~vG|AO)c8`Q4;Wi!7Sv5P1IxQT{ihzT@9_e|EPQPM_u znhHm!G2hQ~WyOfHhD|ZUr%lCD7g~bM%5y@7;)3jh1CueLwW(MFv0jCfMf1g9tXW1B z112mw{tRV~t%RFGF`E&Hh|cceMCFc|IHAcYo+IO>4n9Z_eYe||h`Ir7VM|Rw zB6kkuOSl!WFH|8<2;I03YRoI{`n_4=Cxhfe(JI=|iDiNW!u$m7qI|B51@8Xa80cHw zHMT#3)W}W_U+_U#60CLTed_nY5lK0GYoF2{6Gu$k)=XdryvWEZg4>?>2Kg<2X-5eE z-BK^q9>p!JMED(iFU}QzQrjUal`I|zaJ8b6li}m{S`T;YTX4d(WXzx_hZiNMpd6VP zu@of$&~2jJu*fvK+_-t%%r|FN1D{Db2^qof>aNzUJL32PWoe*NJh6MNNA4USF1z*h zev}Dva~E8U^bci=4=cV|TU=dX{gv!M*&EQHgPV!yGoPGUNCh&hck2!{!L;*VBo*-s zqZ7^8JX3oLZ5qfRmqw05xd<{94oM{)nIUNS)pFns!hKnw`t5$OwcZD(F%?bU5Y=|4 zoSN5SAFad{x*PU+Am!ryFpm_mxfNWv1>c&55R6i2$MY$^5vt1W3_UF>C=0;f#cK|e zXse!`&aMZO481x^ML3aw&+9oT`taMM!m>1LyiDpO9IOv|wwXF0wc8*u983F|XWE`gcGl7|3ZK;aT6rsq_K&sPI<$Pbnj3v zc5rrDmVxc;qRrA`#I;kv;LXEZCp_tvlw#?w66Ud#k(cgDl zt!p8@`XC5WdsdutMyINwnq6!uE?rK=1qq9eIaLJLhSF~Gz;FM6-#p15Vrql6xq5Bw z7VNE+QQN5r-5CF3Ij?P7!>0j!vOk=k zWHQ?vn)E(tL<{5E8WqFFnWMOK*78Ox;!TzuORHw3QHL$~reZjZm*oWO-O&|A9|p_p z9csP?Q2Y2 zRXJBnY+{nR7tO{N%Xh)cM5K2)Hn;SLLVR%&<4QZ}u!a(bJlo#3Jiewb<_^3XbKgDe zS3B2k$_3S`rm-7d_KmC;5$Kc>F!Hc@MT~4o)&_cGGG=ov+`3$XFrLJf2iYWaN@h2)We1%So+i zPDa@0aeh6?oKxTldKX{cmA5yub#_I2^PZs8d_nPDyAs@jQS`^g6XDBh>+a=>E;~Hn z>0Z3zaT)SqUw+X&Uzt5hCC2yDHzilr@w`r3OruaW#hVRiH2aZi$-&|nZyl9+NJX5I zzB%r5XK7qOFg_KHeTAwHEr7-Zi6W__7yQYZ#YquxMpR4ra>U_ocP@Ep&WOtt~$XJSq{{137=sT4v#(DoxbB^N*t&>Bk^hZ?GUnIHg@g3 zJjOsJGebX18QlTiqP2eGqATgnbim%z-KT}ywjrx{Bn!Iv-uxBd7nP{Ukcr6vL(B?q zUal!!6@gzW4XJl1nbGFJoX}$<@t&DnDBL;~FXb|((Qzx!px8ijx{JTMWvh-}bmFs> zihqh99V9z;>sUc*c+iGE4plI`3xf4H)2XDiJ6WCsD(qGVZd~7dwm|7zH9(UwpE;!$ zK28KMBA(#T4g9t^r&QO=X*kLNpOh2VGUHH%6!FIVp8y^N;rlT81;1aN5zFKyWp>*6 zONFp=xca6ba1~2wV~|U_^l*~5G8Rqs*rK(EIwTcKvwpncD&+iIMjhwho!@7Dyp>Kn ziz;DXSF)t~UfAxF@dbiB7Pw{))&%N`S|-4m0^aq;ip(y)jDKR;IjzxKL+TlortETh zY`PHbUO}l2j2?>Tl8nSaPA*xu%lENa`bb#7i_Oz1r0?%= zgXaPIqZ&bvc94W@)-_T{99eHnyZo5#`y=p$`%$8qS5dCt4q^A&Q#uIEqG{UY^=k)J z$DC3dgr?aqmv?j8r6tG^{hhJW8m)Th4wy_Y1XTauSIL_;#A5HJ3SeV>4gvH`uAPoV z<5}1k55#>$drl_6t`haOx(sz`I9G715jC-a?c2?M>OP=v?2ohy!WU-FR%oELuwAR+ zE>vP2Ku5SvC3-g8q{rjsABN`@rb`ZF(4{#XFEl&y^QV8o)~$&r2Xp2QOTIRgMojbBgit=XGEu#W3e~NL!hxY?jZ-ure;Fyr7ZLErs3G)6JgW|L>W+unh=>3H0WL{IK~z(*k-E)7 zM~##dR=gj`NNZs9O`Ws(fq=4R*4Tl&5^3HU4Gx%mxWtv4;RC-4z(1%xlD*N{7O19gh5=12GTBLtdtQJEk)^5gSM$_-FpLGGnJ#_?($NQ?;>x?2YYS zv5r0o*U2O+K(n(rQNn4$e;Qgx}2j2?229eh*WuF*(-H= zkS5{1NPlGtOCSyS6rP;)qs3nWX~4%{rs+GTumt`e?Oh?3j6qHz00000NkvXXu0mjf DDZZn> literal 0 HcmV?d00001 diff --git a/assets/nabu-logo.svg b/assets/nabu-logo.svg new file mode 100644 index 00000000..914b308a --- /dev/null +++ b/assets/nabu-logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 9ef2f3ba7d546fce85d1d7859718f7b1b87cce7e Mon Sep 17 00:00:00 2001 From: Dr Ian Preston Date: Sun, 5 Nov 2023 19:19:18 +0000 Subject: [PATCH 103/218] add logo --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cd52cfc2..e8a895df 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![logo](assets/nabu-logo.png) + # Nabu A minimal Java implementation of [IPFS](https://ipfs.io) From 14d8611d9e2097c7088cf2a5562315ed4feadf83 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Thu, 26 Oct 2023 17:03:10 +0100 Subject: [PATCH 104/218] initial attempt --- .../java/org/peergos/client/PerfTest.java | 116 +++++++++++++ .../java/org/peergos/client/PerfTest2.java | 116 +++++++++++++ .../peergos/protocol/perf/Correlation.java | 12 ++ .../java/org/peergos/protocol/perf/Perf.kt | 156 ++++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 src/main/java/org/peergos/client/PerfTest.java create mode 100644 src/main/java/org/peergos/client/PerfTest2.java create mode 100755 src/main/java/org/peergos/protocol/perf/Correlation.java create mode 100644 src/main/java/org/peergos/protocol/perf/Perf.kt diff --git a/src/main/java/org/peergos/client/PerfTest.java b/src/main/java/org/peergos/client/PerfTest.java new file mode 100644 index 00000000..2dbe8195 --- /dev/null +++ b/src/main/java/org/peergos/client/PerfTest.java @@ -0,0 +1,116 @@ +package org.peergos.client; + +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.Stream; +import io.libp2p.core.StreamPromise; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.dsl.Builder; +import io.libp2p.core.dsl.BuilderJKt; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.core.mux.StreamMuxerProtocol; +import io.libp2p.crypto.keys.Ed25519Kt; +import io.libp2p.security.noise.NoiseXXSecureChannel; +import io.libp2p.transport.tcp.TcpTransport; +import org.peergos.HostBuilder; +import org.peergos.protocol.perf.Perf; +import org.peergos.protocol.perf.PerfController; +import org.peergos.util.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public class PerfTest { + + private Pair> buildHost(boolean isServer) { + String base64PrivKeyStr = "CAESIPtYePRNDRG9RrQ06shBER+MVUyEFJHtrFMiNgabSj1j"; + byte[] privKeyBytes = io.ipfs.multibase.binary.Base64.decodeBase64(base64PrivKeyStr); + org.peergos.HostBuilder builder = new org.peergos.HostBuilder().setIdentity(privKeyBytes); + Optional serverPeerId = Optional.empty(); + if (!isServer) { + serverPeerId = Optional.of(builder.getPeerId()); + PrivKey privKey = Ed25519Kt.generateEd25519KeyPair().getFirst(); + builder = new org.peergos.HostBuilder().setIdentity(privKey.bytes()); + } + return new Pair<>(builder, serverPeerId); + } + + private PerfTest(boolean isServer, String otherServerAddress, String transport, + int uploadBytes, int downloadBytes) throws Exception { + if (!transport.equals("tcp")) { + throw new IllegalArgumentException("Invalid transport protocol: " + transport); + } + int port = 10000 + new Random().nextInt(50000); + String[] serverAddressParts = !isServer ? otherServerAddress.split(":") : + new String[]{"127.0.0.1", "" + port}; + if (!isServer && serverAddressParts.length != 2) { + throw new IllegalArgumentException("Invalid serverAddress! Expecting - :"); + } + String listenAddress = "/ip4/" + serverAddressParts[0] + "/tcp/" + serverAddressParts[1]; + + Pair> hostPair = buildHost(isServer); + HostBuilder serverBuilder = hostPair.left; + PrivKey privKey = serverBuilder.getPrivateKey(); + Host node = BuilderJKt.hostJ(Builder.Defaults.None, b -> { + b.getIdentity().setFactory(() -> privKey); + b.getTransports().add(TcpTransport::new); + b.getSecureChannels().add((k, m) -> new NoiseXXSecureChannel(k, m)); + + List muxers = new ArrayList<>(); + muxers.add(StreamMuxerProtocol.getYamux()); + b.getMuxers().addAll(muxers); + if (isServer) { + b.getProtocols().add(new Perf(uploadBytes, downloadBytes)); + + Multiaddr serverMultiAddr = Multiaddr.fromString(listenAddress); + b.getNetwork().listen(serverMultiAddr.toString()); + b.getConnectionHandlers().add(conn -> System.err.println(conn.localAddress() + + " received connection from " + conn.remoteAddress() + + " on transport " + conn.transport())); + } + }); + node.start().join(); + if (isServer) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + System.err.println("Shutting down server"); + node.stop(); + })); + } + if (isServer) { + System.err.println("Server started. Address: " + listenAddress + " waiting for connection..."); + } else { + PeerId otherHostPeerId = hostPair.right.get(); + StreamPromise perf = node + .getNetwork() + .connect(otherHostPeerId, new Multiaddr(listenAddress)) + .thenApply(it -> it.muxerSession().createStream(new Perf(uploadBytes, downloadBytes))) + .join(); + + Stream perfStream = perf.getStream().get(5, TimeUnit.SECONDS); + PerfController perfCtr = perf.getController().get(5, TimeUnit.SECONDS); + + double latency = perfCtr.perf().join() / 1000.0; + perfStream.close().get(5, TimeUnit.SECONDS); + node.stop(); + System.out.println("{\"type\": \"final\",\"timeSeconds\": " + latency + ",\"uploadBytes\": " + uploadBytes + + ",\"downloadBytes\": " + downloadBytes + "}"); + } + } + public static void main(String[] args) { + try { + boolean isServer = true;//Boolean.parseBoolean(System.getenv("run-server")); + String serverAddress = null;//System.getenv("server-address"); + String transport = "tcp";//System.getenv("transport"); + int uploadBytes = 10 * 1024 * 1024; //Integer.parseInt("1");//System.getenv("upload-bytes"); + int downloadBytes = 5 * 1024 * 1024; //Integer.parseInt("1");;//System.getenv("download-bytes"); + new PerfTest(isServer, serverAddress, transport, uploadBytes, downloadBytes); + } catch (Throwable t) { + t.printStackTrace(); + System.err.println("Unexpected exit: " + t); + System.exit(-1); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/peergos/client/PerfTest2.java b/src/main/java/org/peergos/client/PerfTest2.java new file mode 100644 index 00000000..bb61bea0 --- /dev/null +++ b/src/main/java/org/peergos/client/PerfTest2.java @@ -0,0 +1,116 @@ +package org.peergos.client; + +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.Stream; +import io.libp2p.core.StreamPromise; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.dsl.Builder; +import io.libp2p.core.dsl.BuilderJKt; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.core.mux.StreamMuxerProtocol; +import io.libp2p.crypto.keys.Ed25519Kt; +import io.libp2p.security.noise.NoiseXXSecureChannel; +import io.libp2p.transport.tcp.TcpTransport; +import org.peergos.HostBuilder; +import org.peergos.protocol.perf.Perf; +import org.peergos.protocol.perf.PerfController; +import org.peergos.util.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public class PerfTest2 { + + private Pair> buildHost(boolean isServer) { + String base64PrivKeyStr = "CAESIPtYePRNDRG9RrQ06shBER+MVUyEFJHtrFMiNgabSj1j"; + byte[] privKeyBytes = io.ipfs.multibase.binary.Base64.decodeBase64(base64PrivKeyStr); + HostBuilder builder = new HostBuilder().setIdentity(privKeyBytes); + Optional serverPeerId = Optional.empty(); + if (!isServer) { + serverPeerId = Optional.of(builder.getPeerId()); + PrivKey privKey = Ed25519Kt.generateEd25519KeyPair().getFirst(); + builder = new HostBuilder().setIdentity(privKey.bytes()); + } + return new Pair<>(builder, serverPeerId); + } + + private PerfTest2(boolean isServer, String otherServerAddress, String transport, + int uploadBytes, int downloadBytes) throws Exception { + if (!transport.equals("tcp")) { + throw new IllegalArgumentException("Invalid transport protocol: " + transport); + } + int port = 10000 + new Random().nextInt(50000); + String[] serverAddressParts = !isServer ? otherServerAddress.split(":") : + new String[]{"127.0.0.1", "" + port}; + if (!isServer && serverAddressParts.length != 2) { + throw new IllegalArgumentException("Invalid serverAddress! Expecting - :"); + } + String listenAddress = "/ip4/" + serverAddressParts[0] + "/tcp/" + serverAddressParts[1]; + + Pair> hostPair = buildHost(isServer); + HostBuilder serverBuilder = hostPair.left; + PrivKey privKey = serverBuilder.getPrivateKey(); + Host node = BuilderJKt.hostJ(Builder.Defaults.None, b -> { + b.getIdentity().setFactory(() -> privKey); + b.getTransports().add(TcpTransport::new); + b.getSecureChannels().add((k, m) -> new NoiseXXSecureChannel(k, m)); + + List muxers = new ArrayList<>(); + muxers.add(StreamMuxerProtocol.getYamux()); + b.getMuxers().addAll(muxers); + if (isServer) { + b.getProtocols().add(new Perf(uploadBytes, downloadBytes)); + + Multiaddr serverMultiAddr = Multiaddr.fromString(listenAddress); + b.getNetwork().listen(serverMultiAddr.toString()); + b.getConnectionHandlers().add(conn -> System.err.println(conn.localAddress() + + " received connection from " + conn.remoteAddress() + + " on transport " + conn.transport())); + } + }); + node.start().join(); + if (isServer) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + System.err.println("Shutting down server"); + node.stop(); + })); + } + if (isServer) { + System.err.println("Server started. Address: " + listenAddress + " waiting for connection..."); + } else { + PeerId otherHostPeerId = hostPair.right.get(); + StreamPromise perf = node + .getNetwork() + .connect(otherHostPeerId, new Multiaddr(listenAddress)) + .thenApply(it -> it.muxerSession().createStream(new Perf(uploadBytes, downloadBytes))) + .join(); + + Stream perfStream = perf.getStream().get(5, TimeUnit.SECONDS); + PerfController perfCtr = perf.getController().get(5, TimeUnit.SECONDS); + + double latency = perfCtr.perf().join() / 1000.0; + perfStream.close().get(5, TimeUnit.SECONDS); + node.stop(); + System.out.println("{\"type\": \"final\",\"timeSeconds\": " + latency + ",\"uploadBytes\": " + uploadBytes + + ",\"downloadBytes\": " + downloadBytes + "}"); + } + } + public static void main(String[] args) { + try { + boolean isServer = false;//Boolean.parseBoolean(System.getenv("run-server")); + String serverAddress = "127.0.0.1:32996";//System.getenv("server-address"); + String transport = "tcp";//System.getenv("transport"); + int uploadBytes = 10 * 1024 * 1024; //Integer.parseInt("1");//System.getenv("upload-bytes"); + int downloadBytes = 5 * 1024 * 1024; //Integer.parseInt("1");;//System.getenv("download-bytes"); + new PerfTest2(isServer, serverAddress, transport, uploadBytes, downloadBytes); + } catch (Throwable t) { + t.printStackTrace(); + System.err.println("Unexpected exit: " + t); + System.exit(-1); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/peergos/protocol/perf/Correlation.java b/src/main/java/org/peergos/protocol/perf/Correlation.java new file mode 100755 index 00000000..ad823dbf --- /dev/null +++ b/src/main/java/org/peergos/protocol/perf/Correlation.java @@ -0,0 +1,12 @@ +package org.peergos.protocol.perf; + +public class Correlation { + + public static int id(byte[] input) { + if(input.length < 4) { + throw new IllegalArgumentException("Input length must be >=4"); + } + return (input[0] & 0xFF) | ((input[1] & 0xFF) << 8) | ((input[2] & 0xFF) << 16) | ((input[3] & 0xFF) << 24); + } + +} diff --git a/src/main/java/org/peergos/protocol/perf/Perf.kt b/src/main/java/org/peergos/protocol/perf/Perf.kt new file mode 100644 index 00000000..951971ce --- /dev/null +++ b/src/main/java/org/peergos/protocol/perf/Perf.kt @@ -0,0 +1,156 @@ +package org.peergos.protocol.perf + +import io.libp2p.core.BadPeerException +import io.libp2p.core.ConnectionClosedException +import io.libp2p.core.Libp2pException +import io.libp2p.core.Stream +import io.libp2p.core.multistream.StrictProtocolBinding +import io.libp2p.etc.types.completedExceptionally +import io.libp2p.etc.types.lazyVar +import io.libp2p.etc.types.toByteArray +import io.libp2p.protocol.ProtocolHandler +import io.libp2p.protocol.ProtocolMessageHandler +import io.netty.buffer.ByteBuf +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.ByteToMessageCodec +import java.time.Duration +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + + +/* +Attempt to implement: https://github.com/libp2p/test-plans/tree/master/perf#libp2p-performance-benchmarking + +still to add the following every second... + double latency = (currentMillisecond - startMillisecond) / 1000.0; + System.out.println("{\"type\": \"intermediary\",\"timeSeconds\": " + latency + + ",\"uploadBytes\": " + totalUploadSize + + ",\"downloadBytes\": " + total + "}"); + */ +interface PerfController { + fun perf(): CompletableFuture +} + +class Perf(uploadSize: Int, downloadSize: Int) : PerfBinding(PerfProtocol(uploadSize, downloadSize)) + +open class PerfBinding(perf: PerfProtocol) : + StrictProtocolBinding("/perf/0.1.0", perf) + +class PerfTimeoutException : Libp2pException() + +open class PerfProtocol(var uploadSize: Int, var downloadSize: Int) : ProtocolHandler(Long.MAX_VALUE, Long.MAX_VALUE) { + var timeoutScheduler by lazyVar { Executors.newSingleThreadScheduledExecutor() } + var curTime: () -> Long = { System.currentTimeMillis() } + var random = Random() + var perfTimeout = Duration.ofHours(10) //kev + var totalUploadSize = uploadSize + 4 + var totalDownloadSize = downloadSize + 4 + override fun onStartInitiator(stream: Stream): CompletableFuture { + val handler = PerfInitiator() + stream.pushHandler(PerfCodec()) + stream.pushHandler(handler) + stream.pushHandler(PerfCodec()) + return handler.activeFuture + } + + override fun onStartResponder(stream: Stream): CompletableFuture { + val handler = PerfResponder() + stream.pushHandler(PerfCodec()) + stream.pushHandler(handler) + stream.pushHandler(PerfCodec()) + return CompletableFuture.completedFuture(handler) + } + + open class PerfCodec : ByteToMessageCodec() { + override fun encode(ctx: ChannelHandlerContext?, msg: ByteArray, out: ByteBuf) { + out.writeInt(msg.size) + out.writeBytes(msg) + } + + override fun decode(ctx: ChannelHandlerContext?, msg: ByteBuf, out: MutableList) { + val readerIndex = msg.readerIndex() + if (msg.readableBytes() < 4) { + return + } + val len = msg.readInt() + val readable = msg.readableBytes() + if (readable < len) { + // not enough data to read the full array + // will wait for more ... + msg.readerIndex(readerIndex) + return + } + val data = msg.readSlice(len) + out.add(data.toByteArray()) + } + } + + open inner class PerfResponder : ProtocolMessageHandler, PerfController { + override fun onMessage(stream: Stream, msg: ByteArray) { + val arr = ByteArray(totalDownloadSize) + random.nextBytes(arr) + //copy across correlation id + for (i in 0..3) { + arr[i] = msg[i] + } + stream.writeAndFlush(arr) + } + + override fun perf(): CompletableFuture { + throw Libp2pException("This is perf responder only") + } + } + + open inner class PerfInitiator : ProtocolMessageHandler, PerfController { + val activeFuture = CompletableFuture() + val requests = Collections.synchronizedMap(mutableMapOf>>()) + lateinit var stream: Stream + var closed = false + + override fun onActivated(stream: Stream) { + this.stream = stream + activeFuture.complete(this) + } + + override fun onMessage(stream: Stream, msg: ByteArray) { + val dataS = "" + Correlation.id(msg) + val (sentT, future) = requests.remove(dataS) + ?: throw BadPeerException("Unknown or expired perf data in response: $dataS") + future.complete((curTime() - sentT)) + } + + override fun onClosed(stream: Stream) { + synchronized(requests) { + closed = true + requests.values.forEach { it.second.completeExceptionally(ConnectionClosedException()) } + requests.clear() + timeoutScheduler.shutdownNow() + } + activeFuture.completeExceptionally(ConnectionClosedException()) + } + + override fun perf(): CompletableFuture { + val ret = CompletableFuture() + val arr = ByteArray(totalUploadSize) + random.nextBytes(arr) + val start = curTime() + val dataS = "" + Correlation.id(arr) + synchronized(requests) { + if (closed) return completedExceptionally(ConnectionClosedException()) + requests[dataS] = start to ret + + timeoutScheduler.schedule( + { + requests.remove(dataS)?.second?.completeExceptionally(PerfTimeoutException()) + }, + perfTimeout.toMillis(), + TimeUnit.MILLISECONDS + ) + } + stream.writeAndFlush(arr) + return ret + } + } +} \ No newline at end of file From adfaa3c37a0f36ae6a877cd1ad7788fb543596f1 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Tue, 31 Oct 2023 09:29:47 +0000 Subject: [PATCH 105/218] add ping interop test --- Containerfile | 8 +- pom.xml | 5 + .../org/peergos/client/InteropTestClient.java | 173 ++++++++++++++++++ 3 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/peergos/client/InteropTestClient.java diff --git a/Containerfile b/Containerfile index 18cf3406..38f344fa 100644 --- a/Containerfile +++ b/Containerfile @@ -14,12 +14,10 @@ LABEL org.opencontainers.image.description="Nabu is a minimal Java implementatio LABEL org.opencontainers.image.source="https://github.com/Peergos/nabu" LABEL org.opencontainers.image.licenses="MIT license" -ENV IPFS_PATH=/opt/nabu/.ipfs - WORKDIR /opt/nabu -RUN mkdir -p /opt/nabu/.ipfs + COPY --from=build /opt/nabu/target /opt/nabu -ENTRYPOINT ["java", "-cp", "/opt/nabu/nabu-v0.0.1-SNAPSHOT-jar-with-dependencies.jar", "org.peergos.Nabu", "Addresses.API", "/ip4/0.0.0.0/tcp/5001"] +ENTRYPOINT ["java", "-cp", "/opt/nabu/nabu-v0.6.0-jar-with-dependencies.jar", "org.peergos.client.InteropTestClient"] -EXPOSE 4001 5001 8080 8000 +EXPOSE 4001 diff --git a/pom.xml b/pom.xml index 2be49e0f..f9025cea 100644 --- a/pom.xml +++ b/pom.xml @@ -126,6 +126,11 @@ + + redis.clients + jedis + 4.3.0 + com.github.multiformats java-multiaddr diff --git a/src/main/java/org/peergos/client/InteropTestClient.java b/src/main/java/org/peergos/client/InteropTestClient.java new file mode 100644 index 00000000..1c7d09b7 --- /dev/null +++ b/src/main/java/org/peergos/client/InteropTestClient.java @@ -0,0 +1,173 @@ +package org.peergos.client; + +import identify.pb.IdentifyOuterClass; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.dsl.Builder; +import io.libp2p.core.dsl.BuilderJKt; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.core.multistream.ProtocolBinding; +import io.libp2p.core.mux.StreamMuxerProtocol; +import io.libp2p.crypto.keys.Ed25519Kt; +import io.libp2p.etc.types.ByteArrayExtKt; +import io.libp2p.protocol.Identify; +import io.libp2p.protocol.Ping; +import io.libp2p.protocol.PingController; +import io.libp2p.security.noise.NoiseXXSecureChannel; +import io.libp2p.security.tls.TlsSecureChannel; +import io.libp2p.transport.tcp.TcpTransport; +import io.ipfs.multiaddr.MultiAddress; +import redis.clients.jedis.Jedis; + +import java.io.IOException; +import java.net.*; +import java.security.Security; +import java.util.*; +import java.util.stream.Collectors; + +public class InteropTestClient { + + private static String getLocalIPAddress() throws SocketException { + System.err.println("Getting localIP"); + List interfaces = NetworkInterface.networkInterfaces().collect(Collectors.toList()); + for (NetworkInterface inter : interfaces) { + for (InterfaceAddress addr : inter.getInterfaceAddresses()) { + InetAddress address = addr.getAddress(); + if (! address.isLoopbackAddress() && !(address instanceof Inet6Address)) + return address.getHostAddress(); + } + } + throw new IllegalStateException("Unable to determine local IPAddress"); + } + private InteropTestClient(String transport, String muxer, String security, boolean is_dialer, + String ip, String redis_addr, String test_timeout_seconds) throws Exception { + if (ip == null || ip.length() == 0) + ip = "0.0.0.0"; + if (! is_dialer && ip.equals("0.0.0.0")) + ip = getLocalIPAddress(); + + if (redis_addr == null || redis_addr.length() == 0) { + redis_addr = "redis:6379"; + } + if (test_timeout_seconds == null || test_timeout_seconds.length() == 0) { + test_timeout_seconds = "180"; + } + System.err.println("Parameters: transport=" + transport + " muxer=" + muxer + + " security=" + security + " is_dialer=" + is_dialer + " ip=" + ip + + " redis_addr=" + redis_addr + " test_timeout_seconds=" + test_timeout_seconds); + if (transport == null || muxer == null || security == null) { + throw new IllegalStateException("transport == null || muxer == null || security == null"); + } + int port = 10000 + new Random().nextInt(50000); + Multiaddr address = Multiaddr.fromString("/ip4/" + ip + "/tcp/" + port); + List swarmAddresses = List.of(new MultiAddress(address.toString())); + + List protocols = new ArrayList<>(); + protocols.add(new Ping()); + + PrivKey privKey = Ed25519Kt.generateEd25519KeyPair().getFirst(); + PeerId peerId = PeerId.fromPubKey(privKey.publicKey()); + Multiaddr advertisedAddr = address.withP2P(peerId); + List listenAddrs = new ArrayList<>(); + listenAddrs.addAll(swarmAddresses.stream().map(MultiAddress::toString).collect(Collectors.toList())); + Host node = BuilderJKt.hostJ(Builder.Defaults.None, b -> { + b.getIdentity().setFactory(() -> privKey); + if (transport.equals("tcp")) { + b.getTransports().add(TcpTransport::new); + } + if (security.equals("noise")) { + b.getSecureChannels().add((k, m) -> new NoiseXXSecureChannel(k, m)); + } else if(security.equals("tls")) { + b.getSecureChannels().add((k, m) -> new TlsSecureChannel(k, m, "ECDSA")); + } + List muxers = new ArrayList<>(); + if (muxer.equals("mplex")) { + muxers.add(StreamMuxerProtocol.getMplex()); + } else if(muxer.equals("yamux")) { + muxers.add(StreamMuxerProtocol.getYamux()); + } + b.getMuxers().addAll(muxers); + + for (ProtocolBinding protocol : protocols) { + b.getProtocols().add(protocol); + } + + IdentifyOuterClass.Identify.Builder identifyBuilder = IdentifyOuterClass.Identify.newBuilder() + .setProtocolVersion("ipfs/0.1.0") + .setAgentVersion("nabu/v0.1.0") + .setPublicKey(ByteArrayExtKt.toProtobuf(privKey.publicKey().bytes())) + .addAllListenAddrs(listenAddrs.stream() + .map(Multiaddr::fromString) + .map(Multiaddr::serialize) + .map(ByteArrayExtKt::toProtobuf) + .collect(Collectors.toList())); + for (ProtocolBinding protocol : protocols) { + identifyBuilder = identifyBuilder.addAllProtocols(protocol.getProtocolDescriptor().getAnnounceProtocols()); + } + b.getProtocols().add(new Identify(identifyBuilder.build())); + + for (String listenAddr : listenAddrs) { + b.getNetwork().listen(listenAddr); + } + + b.getConnectionHandlers().add(conn -> System.err.println(conn.localAddress() + + " received connection from " + conn.remoteAddress() + + " on transport " + conn.transport())); + }); + node.start().join(); + Jedis jedis = new Jedis("http://" + redis_addr); + boolean isReady = false; + while (!isReady) { + if ("PONG".equals(jedis.ping())) { + isReady = true; + } else { + System.err.println("waiting for redis to start..."); + Thread.sleep(2000); + } + } + System.err.println("My multiaddr is: " + advertisedAddr); + try { + if (is_dialer) { + List listenerAddrs = jedis.blpop(Integer.parseInt(test_timeout_seconds), "listenerAddr"); + if (listenerAddrs == null || listenerAddrs.isEmpty()) { + throw new IllegalStateException("listenerAddr not set"); + } + String listenerAddrStr = listenerAddrs.get(listenerAddrs.size() -1); + Multiaddr listenerAddr = Multiaddr.fromString(listenerAddrStr); + System.err.println("Other peer multiaddr is: " + listenerAddr); + System.err.println("Sending ping messages to " + listenerAddr); + long handshakeStart = System.currentTimeMillis(); + PingController pinger = new Ping().dial(node, listenerAddr).getController().join(); + long pingRTTMilllis = pinger.ping().join(); + long handshakeEnd = System.currentTimeMillis(); + long handshakePlusOneRTT = handshakeEnd - handshakeStart; + System.err.println("Ping latency " + pingRTTMilllis + "ms"); + String jsonResult = "{\"handshakePlusOneRTTMillis\":" + Double.valueOf(handshakePlusOneRTT) + + ",\"pingRTTMilllis\":" + Double.valueOf(pingRTTMilllis) + "}"; + System.out.println(jsonResult); + } else { + jedis.rpush("listenerAddr", advertisedAddr.toString()); + Thread.sleep(Integer.parseInt(test_timeout_seconds) * 1000); + } + } finally { + System.err.println("Shutting down"); + node.stop(); + } + } + public static void main(String[] args) { + try { + String transport = System.getenv("transport"); //"tcp" + String muxer = System.getenv("muxer"); //"mplex", "yamux" + String security = System.getenv("security"); //"tls", "noise" + boolean is_dialer = Boolean.parseBoolean(System.getenv("is_dialer")); + String ip = System.getenv("ip"); + String redis_addr = System.getenv("redis_addr"); + String test_timeout_seconds = System.getenv("test_timeout_seconds"); + new InteropTestClient(transport, muxer, security, is_dialer, ip, redis_addr, test_timeout_seconds); + } catch (Throwable t) { + System.err.println("Unexpected exit: " + t); + System.exit(-1); + } + } +} From 70aeeda05f005df353ef917426ddb9b61e190e32 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Tue, 31 Oct 2023 10:34:06 +0000 Subject: [PATCH 106/218] update pom for kt --- pom.xml | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index f9025cea..b6f4da75 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ 2.2 v1.4.12 v1.4.4 + 1.9.10 @@ -44,25 +45,7 @@ org.jetbrains.kotlin kotlin-maven-plugin ${kotlin.version} - - - compile - compile - - compile - - - - test-compile - test-compile - - test-compile - - - - - 1.8 - + true org.apache.maven.plugins @@ -126,6 +109,11 @@ + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + redis.clients jedis From de1eb643fdb424d426395da936388e8d14712e0f Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 6 Nov 2023 17:19:58 +0000 Subject: [PATCH 107/218] clean-up --- .../java/org/peergos/client/PerfTest2.java | 116 ------------------ 1 file changed, 116 deletions(-) delete mode 100644 src/main/java/org/peergos/client/PerfTest2.java diff --git a/src/main/java/org/peergos/client/PerfTest2.java b/src/main/java/org/peergos/client/PerfTest2.java deleted file mode 100644 index bb61bea0..00000000 --- a/src/main/java/org/peergos/client/PerfTest2.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.peergos.client; - -import io.libp2p.core.Host; -import io.libp2p.core.PeerId; -import io.libp2p.core.Stream; -import io.libp2p.core.StreamPromise; -import io.libp2p.core.crypto.PrivKey; -import io.libp2p.core.dsl.Builder; -import io.libp2p.core.dsl.BuilderJKt; -import io.libp2p.core.multiformats.Multiaddr; -import io.libp2p.core.mux.StreamMuxerProtocol; -import io.libp2p.crypto.keys.Ed25519Kt; -import io.libp2p.security.noise.NoiseXXSecureChannel; -import io.libp2p.transport.tcp.TcpTransport; -import org.peergos.HostBuilder; -import org.peergos.protocol.perf.Perf; -import org.peergos.protocol.perf.PerfController; -import org.peergos.util.Pair; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -public class PerfTest2 { - - private Pair> buildHost(boolean isServer) { - String base64PrivKeyStr = "CAESIPtYePRNDRG9RrQ06shBER+MVUyEFJHtrFMiNgabSj1j"; - byte[] privKeyBytes = io.ipfs.multibase.binary.Base64.decodeBase64(base64PrivKeyStr); - HostBuilder builder = new HostBuilder().setIdentity(privKeyBytes); - Optional serverPeerId = Optional.empty(); - if (!isServer) { - serverPeerId = Optional.of(builder.getPeerId()); - PrivKey privKey = Ed25519Kt.generateEd25519KeyPair().getFirst(); - builder = new HostBuilder().setIdentity(privKey.bytes()); - } - return new Pair<>(builder, serverPeerId); - } - - private PerfTest2(boolean isServer, String otherServerAddress, String transport, - int uploadBytes, int downloadBytes) throws Exception { - if (!transport.equals("tcp")) { - throw new IllegalArgumentException("Invalid transport protocol: " + transport); - } - int port = 10000 + new Random().nextInt(50000); - String[] serverAddressParts = !isServer ? otherServerAddress.split(":") : - new String[]{"127.0.0.1", "" + port}; - if (!isServer && serverAddressParts.length != 2) { - throw new IllegalArgumentException("Invalid serverAddress! Expecting - :"); - } - String listenAddress = "/ip4/" + serverAddressParts[0] + "/tcp/" + serverAddressParts[1]; - - Pair> hostPair = buildHost(isServer); - HostBuilder serverBuilder = hostPair.left; - PrivKey privKey = serverBuilder.getPrivateKey(); - Host node = BuilderJKt.hostJ(Builder.Defaults.None, b -> { - b.getIdentity().setFactory(() -> privKey); - b.getTransports().add(TcpTransport::new); - b.getSecureChannels().add((k, m) -> new NoiseXXSecureChannel(k, m)); - - List muxers = new ArrayList<>(); - muxers.add(StreamMuxerProtocol.getYamux()); - b.getMuxers().addAll(muxers); - if (isServer) { - b.getProtocols().add(new Perf(uploadBytes, downloadBytes)); - - Multiaddr serverMultiAddr = Multiaddr.fromString(listenAddress); - b.getNetwork().listen(serverMultiAddr.toString()); - b.getConnectionHandlers().add(conn -> System.err.println(conn.localAddress() + - " received connection from " + conn.remoteAddress() + - " on transport " + conn.transport())); - } - }); - node.start().join(); - if (isServer) { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - System.err.println("Shutting down server"); - node.stop(); - })); - } - if (isServer) { - System.err.println("Server started. Address: " + listenAddress + " waiting for connection..."); - } else { - PeerId otherHostPeerId = hostPair.right.get(); - StreamPromise perf = node - .getNetwork() - .connect(otherHostPeerId, new Multiaddr(listenAddress)) - .thenApply(it -> it.muxerSession().createStream(new Perf(uploadBytes, downloadBytes))) - .join(); - - Stream perfStream = perf.getStream().get(5, TimeUnit.SECONDS); - PerfController perfCtr = perf.getController().get(5, TimeUnit.SECONDS); - - double latency = perfCtr.perf().join() / 1000.0; - perfStream.close().get(5, TimeUnit.SECONDS); - node.stop(); - System.out.println("{\"type\": \"final\",\"timeSeconds\": " + latency + ",\"uploadBytes\": " + uploadBytes - + ",\"downloadBytes\": " + downloadBytes + "}"); - } - } - public static void main(String[] args) { - try { - boolean isServer = false;//Boolean.parseBoolean(System.getenv("run-server")); - String serverAddress = "127.0.0.1:32996";//System.getenv("server-address"); - String transport = "tcp";//System.getenv("transport"); - int uploadBytes = 10 * 1024 * 1024; //Integer.parseInt("1");//System.getenv("upload-bytes"); - int downloadBytes = 5 * 1024 * 1024; //Integer.parseInt("1");;//System.getenv("download-bytes"); - new PerfTest2(isServer, serverAddress, transport, uploadBytes, downloadBytes); - } catch (Throwable t) { - t.printStackTrace(); - System.err.println("Unexpected exit: " + t); - System.exit(-1); - } - } -} \ No newline at end of file From eae3b662fc76ad5597fe1d722c316d3f65385318 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 8 Nov 2023 15:34:19 +0000 Subject: [PATCH 108/218] Don't retrieve block for authing bitswap requests --- .../java/org/peergos/BlockRequestAuthoriser.java | 2 +- src/main/java/org/peergos/Nabu.java | 2 +- .../org/peergos/protocol/bitswap/BitswapEngine.java | 12 ++++++------ src/test/java/org/peergos/BitswapMirrorTest.java | 2 +- src/test/java/org/peergos/BitswapTest.java | 8 ++++---- src/test/java/org/peergos/BootstrapTest.java | 2 +- src/test/java/org/peergos/EmbeddedIpfsTest.java | 2 +- src/test/java/org/peergos/FindPeerTest.java | 2 +- src/test/java/org/peergos/FindProviderTest.java | 2 +- src/test/java/org/peergos/HandlerTest.java | 2 +- src/test/java/org/peergos/HttpProxyTest.java | 6 +++--- src/test/java/org/peergos/IpnsTest.java | 4 ++-- src/test/java/org/peergos/KademliaTest.java | 8 ++++---- src/test/java/org/peergos/KuboFindProviderTest.java | 2 +- src/test/java/org/peergos/KuboPingTest.java | 2 +- src/test/java/org/peergos/KuboTest.java | 2 +- src/test/java/org/peergos/P2pHttpChatTest.java | 4 ++-- src/test/java/org/peergos/P2pHttpTest.java | 4 ++-- src/test/java/org/peergos/PingTest.java | 12 ++++++------ src/test/java/org/peergos/ProvideTest.java | 2 +- src/test/java/org/peergos/RelayTest.java | 4 ++-- 21 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/peergos/BlockRequestAuthoriser.java b/src/main/java/org/peergos/BlockRequestAuthoriser.java index f2838ea8..869a78ec 100644 --- a/src/main/java/org/peergos/BlockRequestAuthoriser.java +++ b/src/main/java/org/peergos/BlockRequestAuthoriser.java @@ -6,5 +6,5 @@ public interface BlockRequestAuthoriser { - CompletableFuture allowRead(Cid block, byte[] blockData, Cid sourceNodeId, String auth); + CompletableFuture allowRead(Cid block, Cid sourceNodeId, String auth); } diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 0d4fca40..34df3e5f 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -47,7 +47,7 @@ public Nabu(Args args) throws Exception { AggregatedMetrics.startExporter(config.metrics.address, config.metrics.port); } LOG.info("Starting Nabu version: " + APIHandler.CURRENT_VERSION); - BlockRequestAuthoriser authoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); + BlockRequestAuthoriser authoriser = (c, p, a) -> CompletableFuture.completedFuture(true); Path datastorePath = ipfsPath.resolve("datastore").resolve("h2.datastore"); DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toAbsolutePath().toString()); diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 90539028..0eb87f27 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -177,16 +177,16 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter } if (recent.containsKey(w)) continue; // don't re-send this block as we recently sent it to this peer - Optional block = store.get(c).join(); - if (block.isEmpty()) + boolean blockPresent = store.has(c).join(); + if (! blockPresent) absentBlocks++; else presentBlocks++; - if (block.isPresent() && authoriser.allowRead(c, block.get(), sourcePeerId, auth.orElse("")).join()) { + if (blockPresent && authoriser.allowRead(c, sourcePeerId, auth.orElse("")).join()) { MessageOuterClass.Message.Block blockP = MessageOuterClass.Message.Block.newBuilder() .setPrefix(ByteString.copyFrom(prefixBytes(c))) .setAuth(ByteString.copyFrom(ArrayOps.hexToBytes(auth.orElse("")))) - .setData(ByteString.copyFrom(block.get())) + .setData(ByteString.copyFrom(store.get(c).join().get())) .build(); int blockSize = blockP.getSerializedSize(); if (blockSize + messageSize > maxMessageSize) { @@ -199,7 +199,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter blocks.add(blockP); recent.put(w, true); } else if (sendDontHave) { - if (block.isPresent()) { + if (blockPresent) { deniedWants.put(w, true); LOG.info("Rejecting auth for block " + c + " from " + sourcePeerId.bareMultihash()); } @@ -209,7 +209,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter .build(); presences.add(presence); messageSize += presence.getSerializedSize(); - } else if (block.isPresent()) { + } else if (blockPresent) { deniedWants.put(w, true); LOG.info("Rejecting repeated invalid auth for block " + c + " from " + sourcePeerId.bareMultihash()); } diff --git a/src/test/java/org/peergos/BitswapMirrorTest.java b/src/test/java/org/peergos/BitswapMirrorTest.java index ed537568..7ba9fd36 100644 --- a/src/test/java/org/peergos/BitswapMirrorTest.java +++ b/src/test/java/org/peergos/BitswapMirrorTest.java @@ -23,7 +23,7 @@ public class BitswapMirrorTest { //@Ignore // Local testing only for now - run this prior: ./ipfs pin add zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg public void mirrorTree() throws IOException { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/BitswapTest.java b/src/test/java/org/peergos/BitswapTest.java index 22e47e37..eba5dbf6 100644 --- a/src/test/java/org/peergos/BitswapTest.java +++ b/src/test/java/org/peergos/BitswapTest.java @@ -20,11 +20,11 @@ public class BitswapTest { @Test public void getBlock() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); RamBlockstore blockstore2 = new RamBlockstore(); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node1.start().join(); node2.start().join(); @@ -49,11 +49,11 @@ public void getBlock() { @Test public void blockFlooder() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host flooder = builder1.build(); RamBlockstore blockstore2 = new RamBlockstore(); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, b, p, a) -> CompletableFuture.completedFuture(true), true); + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true), true); Host node2 = builder2.build(); flooder.start().join(); node2.start().join(); diff --git a/src/test/java/org/peergos/BootstrapTest.java b/src/test/java/org/peergos/BootstrapTest.java index 68a034fc..912356b7 100644 --- a/src/test/java/org/peergos/BootstrapTest.java +++ b/src/test/java/org/peergos/BootstrapTest.java @@ -37,7 +37,7 @@ public class BootstrapTest { @Test public void bootstrap() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 504bb2af..8aa4b356 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -59,7 +59,7 @@ public void wildcardListenerAddressesGetExpanded() { } public static EmbeddedIpfs build(List bootstrap, List swarmAddresses) { - BlockRequestAuthoriser blockRequestAuthoriser = (c, b, p, a) -> CompletableFuture.completedFuture(true); + BlockRequestAuthoriser blockRequestAuthoriser = (c, p, a) -> CompletableFuture.completedFuture(true); HostBuilder builder = new HostBuilder().generateIdentity(); PrivKey privKey = builder.getPrivateKey(); PeerId peerId = builder.getPeerId(); diff --git a/src/test/java/org/peergos/FindPeerTest.java b/src/test/java/org/peergos/FindPeerTest.java index 7a524938..91c1610a 100644 --- a/src/test/java/org/peergos/FindPeerTest.java +++ b/src/test/java/org/peergos/FindPeerTest.java @@ -17,7 +17,7 @@ public class FindPeerTest { public void findLongRunningNode() { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/FindProviderTest.java b/src/test/java/org/peergos/FindProviderTest.java index 958df46b..3e2d136d 100644 --- a/src/test/java/org/peergos/FindProviderTest.java +++ b/src/test/java/org/peergos/FindProviderTest.java @@ -20,7 +20,7 @@ public class FindProviderTest { public void findBlockProvider() { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); diff --git a/src/test/java/org/peergos/HandlerTest.java b/src/test/java/org/peergos/HandlerTest.java index afd81613..53c3e57e 100644 --- a/src/test/java/org/peergos/HandlerTest.java +++ b/src/test/java/org/peergos/HandlerTest.java @@ -82,7 +82,7 @@ public void codecTest() { public void findBlockProviderTest() throws IOException { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); HttpServer apiServer = null; diff --git a/src/test/java/org/peergos/HttpProxyTest.java b/src/test/java/org/peergos/HttpProxyTest.java index fd22ddb1..046d0d4d 100644 --- a/src/test/java/org/peergos/HttpProxyTest.java +++ b/src/test/java/org/peergos/HttpProxyTest.java @@ -34,12 +34,12 @@ public void peerid() { public void p2pProxyRequest() throws IOException { InetSocketAddress unusedProxyTarget = new InetSocketAddress("127.0.0.1", 7000); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(new HttpProtocol.Binding(unusedProxyTarget)); Host node1 = builder1.build(); InetSocketAddress proxyTarget = new InetSocketAddress("127.0.0.1", TestPorts.getPort()); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(new HttpProtocol.Binding(proxyTarget)); Host node2 = builder2.build(); node1.start().join(); @@ -95,7 +95,7 @@ public void p2pProxyRequest() throws IOException { public void p2pProxyClientTest() throws Exception { InetSocketAddress unusedProxyTarget = new InetSocketAddress("127.0.0.1", 7000); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(new HttpProtocol.Binding(unusedProxyTarget)); Host node1 = builder1.build(); node1.start().join(); diff --git a/src/test/java/org/peergos/IpnsTest.java b/src/test/java/org/peergos/IpnsTest.java index 9d9d2862..f917cde6 100644 --- a/src/test/java/org/peergos/IpnsTest.java +++ b/src/test/java/org/peergos/IpnsTest.java @@ -24,7 +24,7 @@ public class IpnsTest { public void publishIPNSRecordToKubo() throws IOException { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); @@ -70,7 +70,7 @@ public void publishIPNSRecordToKubo() throws IOException { public void retrieveKuboPublishedIPNS() throws IOException { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 307ef707..96ad00c2 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -20,13 +20,13 @@ public class KademliaTest { public void findOtherNode() throws Exception { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node2.start().join(); IdentifyBuilder.addIdentifyProtocol(node2); @@ -61,13 +61,13 @@ public void findOtherNode() throws Exception { public void ipnsBenchmark() throws Exception { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node2.start().join(); IdentifyBuilder.addIdentifyProtocol(node2); diff --git a/src/test/java/org/peergos/KuboFindProviderTest.java b/src/test/java/org/peergos/KuboFindProviderTest.java index fb8c3a77..68d3b7f1 100644 --- a/src/test/java/org/peergos/KuboFindProviderTest.java +++ b/src/test/java/org/peergos/KuboFindProviderTest.java @@ -20,7 +20,7 @@ public class KuboFindProviderTest { @Test public void findProviderOverYamux() throws IOException { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(1000), - new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)) .addMuxers(List.of(StreamMuxerProtocol.getYamux())); Host node1 = builder1.build(); node1.start().join(); diff --git a/src/test/java/org/peergos/KuboPingTest.java b/src/test/java/org/peergos/KuboPingTest.java index 4d81bbac..ff8b00ae 100644 --- a/src/test/java/org/peergos/KuboPingTest.java +++ b/src/test/java/org/peergos/KuboPingTest.java @@ -22,7 +22,7 @@ public void runPingOverYamux() throws IOException { Host node1 = new HostBuilder() .generateIdentity() .listen(List.of(new MultiAddress("/ip4/0.0.0.0/tcp/" + TestPorts.getPort()))) - .addProtocols(List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))) + .addProtocols(List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))) .addMuxers(List.of(StreamMuxerProtocol.getYamux())) .build(); node1.start().join(); diff --git a/src/test/java/org/peergos/KuboTest.java b/src/test/java/org/peergos/KuboTest.java index f21fb2b0..e1041662 100644 --- a/src/test/java/org/peergos/KuboTest.java +++ b/src/test/java/org/peergos/KuboTest.java @@ -18,7 +18,7 @@ public class KuboTest { @Test public void getBlock() throws IOException { - Bitswap bitswap1 = new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)); + Bitswap bitswap1 = new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)); Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(bitswap1)); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); diff --git a/src/test/java/org/peergos/P2pHttpChatTest.java b/src/test/java/org/peergos/P2pHttpChatTest.java index eb4de071..6347d761 100644 --- a/src/test/java/org/peergos/P2pHttpChatTest.java +++ b/src/test/java/org/peergos/P2pHttpChatTest.java @@ -25,7 +25,7 @@ public void p2pHttpChat() { h.accept(replyOk.retain()); }); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(node1Http); Host node1 = builder1.build(); HttpProtocol.Binding node2Http = new HttpProtocol.Binding((s, req, h) -> { @@ -34,7 +34,7 @@ public void p2pHttpChat() { h.accept(replyOk.retain()); }); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)) .addProtocol(node2Http); Host node2 = builder2.build(); node1.start().join(); diff --git a/src/test/java/org/peergos/P2pHttpTest.java b/src/test/java/org/peergos/P2pHttpTest.java index 2d54c193..0ccf5cad 100644 --- a/src/test/java/org/peergos/P2pHttpTest.java +++ b/src/test/java/org/peergos/P2pHttpTest.java @@ -37,7 +37,7 @@ public void p2pTest() { h.accept(emptyReply.retain()); }); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); builder1 = builder1.addProtocol(node1Http); Host node1 = builder1.build(); node1.start().join(); @@ -73,7 +73,7 @@ public void p2pTest() { HttpProtocol.proxyRequest(req, new InetSocketAddress("127.0.0.1", localPort), h); }); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true)); builder2 = builder2.addProtocol(node2Http); Host node2 = builder2.build(); node2.start().join(); diff --git a/src/test/java/org/peergos/PingTest.java b/src/test/java/org/peergos/PingTest.java index 7d99dcdc..637a39a4 100644 --- a/src/test/java/org/peergos/PingTest.java +++ b/src/test/java/org/peergos/PingTest.java @@ -21,8 +21,8 @@ public class PingTest { @Test public void runPing() { - Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); - Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); + Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); + Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); node2.start().join(); try { @@ -55,11 +55,11 @@ public void runPingEd25519ToRSA() { PrivKey node1Keys = Ed25519Kt.generateEd25519KeyPair().getFirst(); int node1Port = TestPorts.getPort(); Host node1 = build(node1Keys, node1Port, List.of(new Ping(), - new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); + new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); PrivKey node2Keys = RsaKt.generateRsaKeyPair(2048).getFirst(); int node2Port = TestPorts.getPort(); Host node2 = build(node2Keys, node2Port, List.of(new Ping(), - new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); + new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); node2.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); @@ -84,8 +84,8 @@ public void runPingEd25519ToRSA() { @Test public void replyIdentifyOnNewDial() { - Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); - Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); + Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); + Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); IdentifyBuilder.addIdentifyProtocol(node1); node2.start().join(); diff --git a/src/test/java/org/peergos/ProvideTest.java b/src/test/java/org/peergos/ProvideTest.java index 13f46a7b..0a2b322f 100644 --- a/src/test/java/org/peergos/ProvideTest.java +++ b/src/test/java/org/peergos/ProvideTest.java @@ -20,7 +20,7 @@ public class ProvideTest { public void provideBlock() { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); Multihash node1Id = Multihash.deserialize(node1.getPeerId().getBytes()); diff --git a/src/test/java/org/peergos/RelayTest.java b/src/test/java/org/peergos/RelayTest.java index 87c25d3f..b16d893d 100644 --- a/src/test/java/org/peergos/RelayTest.java +++ b/src/test/java/org/peergos/RelayTest.java @@ -19,11 +19,11 @@ public class RelayTest { @Ignore // needs fixed find providers public void relay() { HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); HostBuilder builder2 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, b, p, a) -> CompletableFuture.completedFuture(true)); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node2.start().join(); From 1d3b443e8dbea8b1f1e0103f99f3982297aa5653 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 10 Nov 2023 10:58:48 +0000 Subject: [PATCH 109/218] populate metadataDB if empty --- src/main/java/org/peergos/EmbeddedIpfs.java | 12 +++++------- .../metadatadb/CachingBlockMetadataStore.java | 11 +++++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 6a322497..9773742a 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -144,20 +144,18 @@ public static BlockMetadataStore buildBlockMetadata(Args a) { } } public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMetadataStore meta) { - Blockstore blocks; + Blockstore withMetadb; if (config.datastore.blockMount.prefix.equals("flatfs.datastore")) { - blocks = new FileBlockstore(ipfsPath); + CachingBlockMetadataStore cachedBlocks = new CachingBlockMetadataStore(new FileBlockstore(ipfsPath), meta); + cachedBlocks.updateMetadataStoreIfEmpty(); + withMetadb = cachedBlocks; } else if (config.datastore.blockMount.prefix.equals("s3.datastore")) { S3Blockstore s3blocks = new S3Blockstore(config.datastore.blockMount.getParams(), meta); - blocks = s3blocks; s3blocks.updateMetadataStoreIfEmpty(); + withMetadb = s3blocks; } else { throw new IllegalStateException("Unrecognized datastore prefix: " + config.datastore.blockMount.prefix); } - Blockstore withMetadb = config.datastore.blockMount.prefix.equals("s3.datastore") ? - blocks : - new CachingBlockMetadataStore(blocks, meta); - return typeLimited(filteredBlockStore(withMetadb, config), config); } diff --git a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java index 25ced53d..e672b58b 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java @@ -96,4 +96,15 @@ public CompletableFuture rm(Cid c) { return res; }); } + + public void updateMetadataStoreIfEmpty() { + if (metadata.size() > 0) + return; + List cids = target.refs().join(); + for(Cid c : cids) { + Optional existing = metadata.get(c); + if (existing.isEmpty()) + metadata.put(c, target.getBlockMetadata(c).join()); + } + } } From 4df3a8cf160beec77e088d4b2c2af2c8e22df71f Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 10 Nov 2023 11:52:15 +0000 Subject: [PATCH 110/218] Allow directly listing the blockstore (bypassing the metadata db) --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 +- src/main/java/org/peergos/blockstore/Blockstore.java | 2 +- src/main/java/org/peergos/blockstore/CidBloomFilter.java | 2 +- src/main/java/org/peergos/blockstore/CidInfiniFilter.java | 2 +- src/main/java/org/peergos/blockstore/FileBlockstore.java | 2 +- .../java/org/peergos/blockstore/FilteredBlockstore.java | 4 ++-- .../java/org/peergos/blockstore/ProvidingBlockstore.java | 4 ++-- src/main/java/org/peergos/blockstore/RamBlockstore.java | 2 +- .../java/org/peergos/blockstore/TypeLimitedBlockstore.java | 4 ++-- .../blockstore/metadatadb/CachingBlockMetadataStore.java | 6 ++++-- src/main/java/org/peergos/blockstore/s3/S3Blockstore.java | 4 +++- src/main/java/org/peergos/net/APIHandler.java | 6 +++++- src/test/java/org/peergos/APIServiceTest.java | 2 +- src/test/java/org/peergos/blockstore/BloomTest.java | 2 +- src/test/java/org/peergos/blockstore/InfiniTest.java | 2 +- 15 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 9773742a..6931450e 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -64,7 +64,7 @@ public EmbeddedIpfs(Host node, this.bootstrap = bootstrap; this.blocks = new BitswapBlockService(node, bitswap, dht); this.blockProvider = newBlockProvider.map(q -> new PeriodicBlockProvider(22 * 3600_000L, - () -> blockstore.refs().join().stream(), node, dht, q)); + () -> blockstore.refs(false).join().stream(), node, dht, q)); } public List getBlocks(List wants, Set peers, boolean addToLocal) { diff --git a/src/main/java/org/peergos/blockstore/Blockstore.java b/src/main/java/org/peergos/blockstore/Blockstore.java index 163fb8db..2dcd80dd 100644 --- a/src/main/java/org/peergos/blockstore/Blockstore.java +++ b/src/main/java/org/peergos/blockstore/Blockstore.java @@ -32,7 +32,7 @@ default Cid keyToHash(String key) { CompletableFuture rm(Cid c); - CompletableFuture> refs(); + CompletableFuture> refs(boolean useBlockstore); CompletableFuture bloomAdd(Cid cid); diff --git a/src/main/java/org/peergos/blockstore/CidBloomFilter.java b/src/main/java/org/peergos/blockstore/CidBloomFilter.java index 1b738b09..9d4e7c61 100644 --- a/src/main/java/org/peergos/blockstore/CidBloomFilter.java +++ b/src/main/java/org/peergos/blockstore/CidBloomFilter.java @@ -24,7 +24,7 @@ public Cid add(Cid c) { } public static CidBloomFilter build(Blockstore bs, double falsePositiveRate) { - List refs = bs.refs().join(); + List refs = bs.refs(false).join(); BloomFilter bloom = new BloomFilter<>(falsePositiveRate, refs.size()); refs.forEach(bloom::add); return new CidBloomFilter(bloom); diff --git a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java index a838e035..0d6327be 100644 --- a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java +++ b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java @@ -33,7 +33,7 @@ public static CidInfiniFilter build(Blockstore bs) { } public static CidInfiniFilter build(Blockstore bs, double falsePositiveRate) { - List refs = bs.refs().join(); + List refs = bs.refs(false).join(); int nBlocks = refs.size()*5/4; // increase by 25% to avoid expansion during build int nextPowerOfTwo = Math.max(17, (int) (1 + Math.log(nBlocks) / Math.log(2))); double expansionAlpha = 0.8; diff --git a/src/main/java/org/peergos/blockstore/FileBlockstore.java b/src/main/java/org/peergos/blockstore/FileBlockstore.java index b34c24da..b1a9b454 100644 --- a/src/main/java/org/peergos/blockstore/FileBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FileBlockstore.java @@ -134,7 +134,7 @@ public CompletableFuture bloomAdd(Cid cid) { } @Override - public CompletableFuture> refs() { + public CompletableFuture> refs(boolean useBlockstore) { List result = new ArrayList<>(); try (Stream walk = Files.walk(blocksRoot)) { result = walk.filter(f -> Files.isRegularFile(f) && f.getFileName().toString().endsWith(BLOCK_FILE_SUFFIX)) diff --git a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java index 3952e431..ab58a2bd 100644 --- a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java @@ -57,8 +57,8 @@ public CompletableFuture rm(Cid c) { } @Override - public CompletableFuture> refs() { - return blocks.refs(); + public CompletableFuture> refs(boolean useBlockstore) { + return blocks.refs(useBlockstore); } public static FilteredBlockstore bloomBased(Blockstore source, double falsePositiveRate) { diff --git a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java index ffd0acb1..fb54fa68 100644 --- a/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java +++ b/src/main/java/org/peergos/blockstore/ProvidingBlockstore.java @@ -45,8 +45,8 @@ public CompletableFuture rm(Cid c) { } @Override - public CompletableFuture> refs() { - return target.refs(); + public CompletableFuture> refs(boolean useBlockstore) { + return target.refs(useBlockstore); } @Override diff --git a/src/main/java/org/peergos/blockstore/RamBlockstore.java b/src/main/java/org/peergos/blockstore/RamBlockstore.java index c7f2f483..41b69e06 100644 --- a/src/main/java/org/peergos/blockstore/RamBlockstore.java +++ b/src/main/java/org/peergos/blockstore/RamBlockstore.java @@ -54,7 +54,7 @@ public CompletableFuture bloomAdd(Cid cid) { } @Override - public CompletableFuture> refs() { + public CompletableFuture> refs(boolean useBlockstore) { return CompletableFuture.completedFuture(new ArrayList(blocks.keySet())); } diff --git a/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java b/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java index 623a334c..600386bc 100644 --- a/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java +++ b/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java @@ -69,8 +69,8 @@ public CompletableFuture rm(Cid cid) { } @Override - public CompletableFuture> refs() { - return blocks.refs(); + public CompletableFuture> refs(boolean useBlockstore) { + return blocks.refs(useBlockstore); } @Override diff --git a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java index e672b58b..659103f0 100644 --- a/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java +++ b/src/main/java/org/peergos/blockstore/metadatadb/CachingBlockMetadataStore.java @@ -83,7 +83,9 @@ public CompletableFuture bloomAdd(Cid cid) { } @Override - public CompletableFuture> refs() { + public CompletableFuture> refs(boolean useBlockstore) { + if (useBlockstore) + return target.refs(true); return CompletableFuture.completedFuture(metadata.list().collect(Collectors.toList())); } @@ -100,7 +102,7 @@ public CompletableFuture rm(Cid c) { public void updateMetadataStoreIfEmpty() { if (metadata.size() > 0) return; - List cids = target.refs().join(); + List cids = target.refs(true).join(); for(Cid c : cids) { Optional existing = metadata.get(c); if (existing.isEmpty()) diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index e050b76e..aff3b92d 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -409,7 +409,9 @@ public CompletableFuture bloomAdd(Cid cid) { } @Override - public CompletableFuture> refs() { + public CompletableFuture> refs(boolean useBlockstore) { + if (useBlockstore) + return directRefs(); return CompletableFuture.completedFuture(blockMetadata.list().collect(Collectors.toList())); } diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index d05fa797..716cd1b8 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -161,7 +161,11 @@ public void handleCallToAPI(HttpExchange httpExchange) { } case REFS_LOCAL: { // https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-refs-local AggregatedMetrics.API_REFS_LOCAL.inc(); - List refs = ipfs.blockstore.refs().join(); + Boolean useBlockStore = Optional.ofNullable(params.get("use-block-store")) + .map(a -> a.get(0)) + .map(Boolean::parseBoolean) + .orElse(false); + List refs = ipfs.blockstore.refs(useBlockStore).join(); StringBuilder sb = new StringBuilder(); for (Cid cid : refs) { Map entry = new HashMap<>(); diff --git a/src/test/java/org/peergos/APIServiceTest.java b/src/test/java/org/peergos/APIServiceTest.java index e8433115..9ecd3db8 100644 --- a/src/test/java/org/peergos/APIServiceTest.java +++ b/src/test/java/org/peergos/APIServiceTest.java @@ -70,7 +70,7 @@ public static void runAPIServiceTest(Blockstore blocks) { Assert.assertTrue("block retrieved", blockRetrieved.size() == 1); Assert.assertTrue("block is as expected", text.equals(new String(blockRetrieved.get(0).block))); - List localRefs = ipfs.blockstore.refs().join(); + List localRefs = ipfs.blockstore.refs(false).join(); for (Cid ref : localRefs) { List res = ipfs.getBlocks(List.of(new Want(ref)), Collections.emptySet(), false); Assert.assertTrue("ref retrieved", res.size() == 1); diff --git a/src/test/java/org/peergos/blockstore/BloomTest.java b/src/test/java/org/peergos/blockstore/BloomTest.java index fb6fc2ec..6ef62c5f 100644 --- a/src/test/java/org/peergos/blockstore/BloomTest.java +++ b/src/test/java/org/peergos/blockstore/BloomTest.java @@ -19,7 +19,7 @@ public void bloom() { CidBloomFilter bloom = CidBloomFilter.build(bs); long t2 = System.currentTimeMillis(); System.out.println("Building filter took: " + (t2-t1)+ "ms"); - List refs = bs.refs().join(); + List refs = bs.refs(false).join(); for (Cid ref : refs) { Assert.assertTrue(bloom.has(ref)); } diff --git a/src/test/java/org/peergos/blockstore/InfiniTest.java b/src/test/java/org/peergos/blockstore/InfiniTest.java index 28afe8e3..ba45bb68 100644 --- a/src/test/java/org/peergos/blockstore/InfiniTest.java +++ b/src/test/java/org/peergos/blockstore/InfiniTest.java @@ -19,7 +19,7 @@ public void infini() { CidInfiniFilter infini = CidInfiniFilter.build(bs, 0.01); long t2 = System.currentTimeMillis(); System.out.println("Building filter took: " + (t2-t1)+ "ms"); - List refs = bs.refs().join(); + List refs = bs.refs(false).join(); for (Cid ref : refs) { Assert.assertTrue(infini.has(ref)); } From 91c71d1518ee8a280a0695c1e1471d1f7a8811e9 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 10 Nov 2023 12:25:24 +0000 Subject: [PATCH 111/218] Allow to not update metadata db --- src/main/java/org/peergos/EmbeddedIpfs.java | 8 +++++--- src/main/java/org/peergos/Nabu.java | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 6931450e..e0cb0541 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -143,15 +143,17 @@ public static BlockMetadataStore buildBlockMetadata(Args a) { throw new RuntimeException(e); } } - public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMetadataStore meta) { + public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMetadataStore meta, boolean updateMetadb) { Blockstore withMetadb; if (config.datastore.blockMount.prefix.equals("flatfs.datastore")) { CachingBlockMetadataStore cachedBlocks = new CachingBlockMetadataStore(new FileBlockstore(ipfsPath), meta); - cachedBlocks.updateMetadataStoreIfEmpty(); + if (updateMetadb) + cachedBlocks.updateMetadataStoreIfEmpty(); withMetadb = cachedBlocks; } else if (config.datastore.blockMount.prefix.equals("s3.datastore")) { S3Blockstore s3blocks = new S3Blockstore(config.datastore.blockMount.getParams(), meta); - s3blocks.updateMetadataStoreIfEmpty(); + if (updateMetadb) + s3blocks.updateMetadataStoreIfEmpty(); withMetadb = s3blocks; } else { throw new IllegalStateException("Unrecognized datastore prefix: " + config.datastore.blockMount.prefix); diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 34df3e5f..acbf69cc 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -53,7 +53,7 @@ public Nabu(Args args) throws Exception { DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toAbsolutePath().toString()); BlockMetadataStore meta = buildBlockMetadata(args); EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, - buildBlockStore(config, ipfsPath, meta), + buildBlockStore(config, ipfsPath, meta, true), true, config.addresses.getSwarmAddresses(), config.bootstrap.getBootstrapAddresses(), From ccec664200b03378b037bf5a95a96f018dc391f6 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 11 Nov 2023 17:01:44 +0000 Subject: [PATCH 112/218] Limit block types in TypeLimitedBlockstore.refs() Filter 0 size blocks in refs --- src/main/java/org/peergos/blockstore/FileBlockstore.java | 4 +++- .../java/org/peergos/blockstore/TypeLimitedBlockstore.java | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/FileBlockstore.java b/src/main/java/org/peergos/blockstore/FileBlockstore.java index b1a9b454..4ebc1e9f 100644 --- a/src/main/java/org/peergos/blockstore/FileBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FileBlockstore.java @@ -137,7 +137,9 @@ public CompletableFuture bloomAdd(Cid cid) { public CompletableFuture> refs(boolean useBlockstore) { List result = new ArrayList<>(); try (Stream walk = Files.walk(blocksRoot)) { - result = walk.filter(f -> Files.isRegularFile(f) && f.getFileName().toString().endsWith(BLOCK_FILE_SUFFIX)) + result = walk.filter(f -> Files.isRegularFile(f) && + f.toFile().length() > 0 && + f.getFileName().toString().endsWith(BLOCK_FILE_SUFFIX)) .collect(Collectors.toList()); } catch (IOException ioe) { LOG.log(Level.WARNING, "Unable to retrieve local refs: " + ioe); diff --git a/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java b/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java index 600386bc..416fe864 100644 --- a/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java +++ b/src/main/java/org/peergos/blockstore/TypeLimitedBlockstore.java @@ -9,6 +9,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.stream.*; public class TypeLimitedBlockstore implements Blockstore { @@ -70,7 +71,9 @@ public CompletableFuture rm(Cid cid) { @Override public CompletableFuture> refs(boolean useBlockstore) { - return blocks.refs(useBlockstore); + return blocks.refs(useBlockstore).thenApply(res -> res.stream() + .filter(c -> allowedCodecs.contains(c.codec)) + .collect(Collectors.toList())); } @Override From 6e0faf9e4fe8481af47dbd1e42d1f85fbfc68a26 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 13 Nov 2023 07:35:47 +0000 Subject: [PATCH 113/218] Version v0.7.0 --- src/main/java/org/peergos/net/APIHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index 716cd1b8..898f2559 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -13,7 +13,7 @@ public class APIHandler extends Handler { public static final String API_URL = "/api/v0/"; - public static final Version CURRENT_VERSION = Version.parse("0.6.0"); + public static final Version CURRENT_VERSION = Version.parse("0.7.0"); private static final Logger LOG = Logging.LOG(); private static final boolean LOGGING = true; From 78528a71e3cd3f0d9b0322a25567956471ffd3f1 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 13 Nov 2023 07:40:20 +0000 Subject: [PATCH 114/218] update pom version --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b6f4da75..8f579494 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.6.0 + v0.7.0 UTF-8 @@ -214,4 +214,4 @@ 0.16.0 - \ No newline at end of file + From 0205f45619ed644a82069ef8f7f46d1505c64361 Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Mon, 20 Nov 2023 04:03:26 +0000 Subject: [PATCH 115/218] use alternate method to list network interfaces --- src/main/java/org/peergos/protocol/IdentifyBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/IdentifyBuilder.java b/src/main/java/org/peergos/protocol/IdentifyBuilder.java index f2a82dfa..402120c6 100644 --- a/src/main/java/org/peergos/protocol/IdentifyBuilder.java +++ b/src/main/java/org/peergos/protocol/IdentifyBuilder.java @@ -60,7 +60,7 @@ public static List expandWildcardAddresses(Multiaddr addr) { public static List listNetworkAddresses(boolean includeIp6, Multiaddr addr) { try { - return NetworkInterface.networkInterfaces() + return Collections.list(NetworkInterface.getNetworkInterfaces()).stream() .flatMap(net -> net.getInterfaceAddresses().stream() .map(InterfaceAddress::getAddress) .filter(ip -> includeIp6 || ip instanceof Inet4Address)) From d63b73f5d5ffd123e6b8db926d12b3945685bc67 Mon Sep 17 00:00:00 2001 From: Dr Ian Preston Date: Thu, 23 Nov 2023 10:06:31 +0000 Subject: [PATCH 116/218] Update README.md add usage --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index e8a895df..6805e77e 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,40 @@ for Maven, add the following sections to your pom.xml (replacing $LATEST_VERSION ``` +### Embedded usage +```java + List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/4001")); + List bootstrapAddresses = List.of(new MultiAddress("/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa")); + BlockRequestAuthoriser authoriser = (cid, peerid, auth) -> CompletableFuture.completedFuture(true); + HostBuilder builder = new HostBuilder().generateIdentity(); + PrivKey privKey = builder.getPrivateKey(); + PeerId peerId = builder.getPeerId(); + IdentitySection identity = new IdentitySection(privKey.bytes(), peerId); + boolean provideBlocks = true; + SocketAddress httpTarget = new InetSocketAddress("localhost", 10000); + Optional httpProxyTarget = + Optional.of((s, req, h) -> HttpProtocol.proxyRequest(req, httpTarget, h)); + + EmbeddedIpfs ipfs = EmbeddedIpfs.build(new RamRecordStore(), + new FileBlockstore(Path.of("/home/alice/ipfs")), + provideBlocks, + swarmAddresses, + bootstrapAddresses, + identity, + authoriser, + httpProxyTarget + ); + ipfs.start(); + + List wants = List.of(new Want(Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"))); + Set retrieveFrom = Set.of(PeerId.fromBase58("QmVdFZgHnEgcedCS2G2ZNiEN59LuVrnRm7z3yXtEBv2XiF")); + boolean addToLocal = true; + List blocks = ipfs.getBlocks(wants, retrieveFrom, addToLocal); + byte[] data = blocks.get(0).block; +``` +### Demo app +If you want a working example app you can fork, have a look at our [chat example](https://github.com/Peergos/nabu-chat). This is a simple CLI app where two users exchange peerid (out of band) and then connect and send messages via p2p http requests, which are printed to the console. ### S3 Blockstore From 8d13045a752b015d28553a7c7088a9b94f2105bc Mon Sep 17 00:00:00 2001 From: Dr Ian Preston Date: Thu, 23 Nov 2023 10:08:13 +0000 Subject: [PATCH 117/218] Update README.md --- README.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 6805e77e..4ade40e6 100644 --- a/README.md +++ b/README.md @@ -65,35 +65,35 @@ for Maven, add the following sections to your pom.xml (replacing $LATEST_VERSION ``` ### Embedded usage ```java - List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/4001")); - List bootstrapAddresses = List.of(new MultiAddress("/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa")); - BlockRequestAuthoriser authoriser = (cid, peerid, auth) -> CompletableFuture.completedFuture(true); - HostBuilder builder = new HostBuilder().generateIdentity(); - PrivKey privKey = builder.getPrivateKey(); - PeerId peerId = builder.getPeerId(); - IdentitySection identity = new IdentitySection(privKey.bytes(), peerId); - boolean provideBlocks = true; - - SocketAddress httpTarget = new InetSocketAddress("localhost", 10000); - Optional httpProxyTarget = - Optional.of((s, req, h) -> HttpProtocol.proxyRequest(req, httpTarget, h)); +List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/4001")); +List bootstrapAddresses = List.of(new MultiAddress("/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa")); +BlockRequestAuthoriser authoriser = (cid, peerid, auth) -> CompletableFuture.completedFuture(true); +HostBuilder builder = new HostBuilder().generateIdentity(); +PrivKey privKey = builder.getPrivateKey(); +PeerId peerId = builder.getPeerId(); +IdentitySection identity = new IdentitySection(privKey.bytes(), peerId); +boolean provideBlocks = true; + +SocketAddress httpTarget = new InetSocketAddress("localhost", 10000); +Optional httpProxyTarget = + Optional.of((s, req, h) -> HttpProtocol.proxyRequest(req, httpTarget, h)); - EmbeddedIpfs ipfs = EmbeddedIpfs.build(new RamRecordStore(), - new FileBlockstore(Path.of("/home/alice/ipfs")), - provideBlocks, - swarmAddresses, - bootstrapAddresses, - identity, - authoriser, - httpProxyTarget - ); - ipfs.start(); - - List wants = List.of(new Want(Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"))); - Set retrieveFrom = Set.of(PeerId.fromBase58("QmVdFZgHnEgcedCS2G2ZNiEN59LuVrnRm7z3yXtEBv2XiF")); - boolean addToLocal = true; - List blocks = ipfs.getBlocks(wants, retrieveFrom, addToLocal); - byte[] data = blocks.get(0).block; +EmbeddedIpfs ipfs = EmbeddedIpfs.build(new RamRecordStore(), + new FileBlockstore(Path.of("/home/alice/ipfs")), + provideBlocks, + swarmAddresses, + bootstrapAddresses, + identity, + authoriser, + httpProxyTarget +); +ipfs.start(); + +List wants = List.of(new Want(Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"))); +Set retrieveFrom = Set.of(PeerId.fromBase58("QmVdFZgHnEgcedCS2G2ZNiEN59LuVrnRm7z3yXtEBv2XiF")); +boolean addToLocal = true; +List blocks = ipfs.getBlocks(wants, retrieveFrom, addToLocal); +byte[] data = blocks.get(0).block; ``` ### Demo app If you want a working example app you can fork, have a look at our [chat example](https://github.com/Peergos/nabu-chat). This is a simple CLI app where two users exchange peerid (out of band) and then connect and send messages via p2p http requests, which are printed to the console. From 4873bece359e073ee87a1b5b57a314e5bc5205f5 Mon Sep 17 00:00:00 2001 From: Dr Ian Preston Date: Thu, 23 Nov 2023 10:10:39 +0000 Subject: [PATCH 118/218] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ade40e6..f3e5ff3f 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,15 @@ Currently implemented properties: * bloom/[infini filtered](https://www.rasmuspagh.net/papers/infinifilter.pdf) blockstore * connect bitswap to kademlia for discovery, with a faster version with supplied peerids * configurable cid publishing function +* Android compatibility +* example serverless chat app using p2p http proxy for Android In the future we will add: * circuit-relay * dcutr (direct connection upgrade through relay) * AutoRelay * mDNS peer discovery -* Android compatibility -* example serverless chat app using p2p http proxy for Android and iOS +* example iOS chat app * QUIC transport (and encryption and multiplexing) ## Usage From 4d02ba66394cde60d0d54074d17b67d0db7721fc Mon Sep 17 00:00:00 2001 From: Dr Ian Preston Date: Thu, 23 Nov 2023 10:20:04 +0000 Subject: [PATCH 119/218] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f3e5ff3f..6acfa0ca 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Currently implemented properties: * configurable cid publishing function * Android compatibility * example serverless chat app using p2p http proxy for Android +* interop tests with other implementations - https://github.com/libp2p/test-plans/ In the future we will add: * circuit-relay From f2a243cc3c46be9f88f13a17de7a637e6445aebc Mon Sep 17 00:00:00 2001 From: kevodwyer Date: Fri, 24 Nov 2023 08:58:01 +0000 Subject: [PATCH 120/218] add maven build command --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 6acfa0ca..9ef6349c 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,15 @@ for Maven, add the following sections to your pom.xml (replacing $LATEST_VERSION ``` + + +Build using Maven + +``` +mvn package -Dmaven.test.skip=true +``` + + ### Embedded usage ```java List swarmAddresses = List.of(new MultiAddress("/ip6/::/tcp/4001")); From 7c46cfcff7dd2d7505804d9e160c419af318ba68 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 25 Nov 2023 17:43:06 +0000 Subject: [PATCH 121/218] Update jvm-libp2p to include yamux refactor --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8f579494..1e2a92c4 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ com.github.peergos jvm-libp2p - 0.16.0 + 0.16.3 org.bouncycastle From 217e2bf7dbcc81b270d1bb43df5aaa4035787cee Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 28 Nov 2023 16:51:47 +0000 Subject: [PATCH 122/218] ~halve ram usage of RamAddressBook and kademlia responses --- src/main/java/org/peergos/RamAddressBook.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/RamAddressBook.java b/src/main/java/org/peergos/RamAddressBook.java index 1d157272..29ce34b8 100644 --- a/src/main/java/org/peergos/RamAddressBook.java +++ b/src/main/java/org/peergos/RamAddressBook.java @@ -7,6 +7,8 @@ import java.util.*; import java.util.concurrent.*; +import java.util.stream.*; +import java.util.stream.Stream; public class RamAddressBook implements AddressBook { @@ -22,7 +24,7 @@ public CompletableFuture addAddrs(@NotNull PeerId peerId, long ttl, @NotNu addresses.putIfAbsent(peerId, new HashSet<>()); Set val = addresses.get(peerId); synchronized (val) { - val.addAll(Arrays.asList(multiaddrs)); + val.addAll(withoutPeerId(multiaddrs, peerId)); } return CompletableFuture.completedFuture(null); } @@ -37,8 +39,23 @@ public CompletableFuture> getAddrs(@NotNull PeerId peerId) @Override public CompletableFuture setAddrs(@NotNull PeerId peerId, long ttl, @NotNull Multiaddr... multiaddrs) { Set val = new HashSet<>(); - val.addAll(Arrays.asList(multiaddrs)); + val.addAll(withoutPeerId(multiaddrs, peerId)); addresses.put(peerId, val); return CompletableFuture.completedFuture(null); } + + private static List withoutPeerId(Multiaddr[] in, PeerId id) { + byte[] peerId = id.getBytes(); + return Stream.of(in) + .map(a -> withoutPeerId(a, peerId)) + .collect(Collectors.toList()); + } + + private static Multiaddr withoutPeerId(Multiaddr in, byte[] peerId) { + List comp = in.getComponents(); + MultiaddrComponent last = comp.get(comp.size() - 1); + if (last.getProtocol() == Protocol.P2P && Arrays.equals(last.getValue(), peerId)) + return new Multiaddr(comp.subList(0, comp.size() - 1)); + return in; + } } From 23168809944e5cf3c4cd4141c9e6be959fde9276 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 29 Nov 2023 11:52:44 +0000 Subject: [PATCH 123/218] Release v0.7.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1e2a92c4..08a25710 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.0 + v0.7.1 UTF-8 From 961753815ba28b54b036b33284d299072b2245d4 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 4 Dec 2023 15:51:00 +0000 Subject: [PATCH 124/218] Don't return peers with no addresses in kademlia --- src/main/java/org/peergos/protocol/dht/KademliaEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index d4596998..92a285dd 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -86,6 +86,7 @@ public List getKClosestPeers(byte[] key) { List addrs = new ArrayList<>(addressBook.getAddrs(PeerId.fromBase58(n.getLink())).join()); return new PeerAddresses(Multihash.fromBase58(n.getLink()), addrs); }) + .filter(p -> ! p.addresses.isEmpty()) .collect(Collectors.toList()); } From ea04d0c16a600b787817b67b1ef659021dbfe791 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 4 Dec 2023 15:52:53 +0000 Subject: [PATCH 125/218] Also filter legacy IPFS components of multiaddrs --- src/main/java/org/peergos/RamAddressBook.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/RamAddressBook.java b/src/main/java/org/peergos/RamAddressBook.java index 29ce34b8..a67707d5 100644 --- a/src/main/java/org/peergos/RamAddressBook.java +++ b/src/main/java/org/peergos/RamAddressBook.java @@ -54,7 +54,8 @@ private static List withoutPeerId(Multiaddr[] in, PeerId id) { private static Multiaddr withoutPeerId(Multiaddr in, byte[] peerId) { List comp = in.getComponents(); MultiaddrComponent last = comp.get(comp.size() - 1); - if (last.getProtocol() == Protocol.P2P && Arrays.equals(last.getValue(), peerId)) + if ((last.getProtocol() == Protocol.P2P || last.getProtocol() == Protocol.IPFS) && + Arrays.equals(last.getValue(), peerId)) return new Multiaddr(comp.subList(0, comp.size() - 1)); return in; } From 6646db7308d38830af8c29bdd24549ae257f1a36 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 4 Dec 2023 23:34:59 +0000 Subject: [PATCH 126/218] Don't add incoming kademlia peers to ruting table! Only add them if they announce kademlia (in Identify) and they answer a kademlia query on a new stream. --- src/main/java/org/peergos/HostBuilder.java | 25 ++++++++++++++++--- .../org/peergos/protocol/dht/Kademlia.java | 4 ++- .../peergos/protocol/dht/KademliaEngine.java | 6 ++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/peergos/HostBuilder.java b/src/main/java/org/peergos/HostBuilder.java index 1d4eeab0..da8105f0 100644 --- a/src/main/java/org/peergos/HostBuilder.java +++ b/src/main/java/org/peergos/HostBuilder.java @@ -173,6 +173,10 @@ public static Host build(PrivKey privKey, b.getConnectionHandlers().add((ConnectionHandler) protocol); } + Optional wan = protocols.stream() + .filter(p -> p instanceof Kademlia && p.getProtocolDescriptor().getAnnounceProtocols().contains("/ipfs/kad/1.0.0")) + .map(p -> (Kademlia) p) + .findFirst(); // Send an identify req on all new incoming connections b.getConnectionHandlers().add(connection -> { PeerId remotePeer = connection.secureSession().getRemoteId(); @@ -184,10 +188,23 @@ public static Host build(PrivKey privKey, .createStream(new IdentifyBinding(new IdentifyProtocol())); stream.getController() .thenCompose(IdentifyController::id) - .thenApply(remoteId -> addrs.addAddrs(remotePeer, 0, remoteId.getListenAddrsList() - .stream() - .map(bytes -> Multiaddr.deserialize(bytes.toByteArray())) - .toArray(Multiaddr[]::new))); + .thenAccept(remoteId -> { + Multiaddr[] remoteAddrs = remoteId.getListenAddrsList() + .stream() + .map(bytes -> Multiaddr.deserialize(bytes.toByteArray())) + .toArray(Multiaddr[]::new); + addrs.getAddrs(remotePeer).thenAccept(existing -> { + addrs.addAddrs(remotePeer, 0, remoteAddrs); + List protocolIds = remoteId.getProtocolsList().stream().collect(Collectors.toList()); + if (protocolIds.contains(Kademlia.WAN_DHT_ID) && wan.isPresent()) { + // add to kademlia routing table iffi + // 1) we haven't already dialled them + // 2) they accept a new kademlia stream + if (! existing.isEmpty()) + connection.muxerSession().createStream(wan.get()); + } + }); + }); }); for (String listenAddr : listenAddrs) { diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 7cfca9c7..47e13716 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -26,12 +26,14 @@ public class Kademlia extends StrictProtocolBinding implemen private static final Logger LOG = Logging.LOG(); public static final int BOOTSTRAP_PERIOD_MILLIS = 300_000; + public static final String WAN_DHT_ID = "/ipfs/kad/1.0.0"; + public static final String LAN_DHT_ID = "/ipfs/lan/kad/1.0.0"; private final KademliaEngine engine; private final boolean localDht; private AddressBook addressBook; public Kademlia(KademliaEngine dht, boolean localOnly) { - super("/ipfs/" + (localOnly ? "lan/" : "") + "kad/1.0.0", new KademliaProtocol(dht)); + super(localOnly ? LAN_DHT_ID : WAN_DHT_ID, new KademliaProtocol(dht)); this.engine = dht; this.localDht = localOnly; } diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 92a285dd..68e5f59d 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -61,10 +61,14 @@ public void setAddressBook(AddressBook addrs) { } public synchronized void addOutgoingConnection(PeerId peer) { - router.touch(Instant.now(), new Node(Id.create(Hash.sha256(peer.getBytes()), 256), peer.toString())); + addToRoutingTable(peer); } public synchronized void addIncomingConnection(PeerId peer) { + // don't auto add incoming kademlia connections to routing table + } + + private void addToRoutingTable(PeerId peer) { router.touch(Instant.now(), new Node(Id.create(Hash.sha256(peer.getBytes()), 256), peer.toString())); } From b70d0435cc29142877e90d6711fd0f08d0ec6268 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 5 Dec 2023 16:49:04 +0000 Subject: [PATCH 127/218] Fix sense of dialing incoming connections for kademlia --- src/main/java/org/peergos/HostBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/HostBuilder.java b/src/main/java/org/peergos/HostBuilder.java index da8105f0..a71054d0 100644 --- a/src/main/java/org/peergos/HostBuilder.java +++ b/src/main/java/org/peergos/HostBuilder.java @@ -200,7 +200,7 @@ public static Host build(PrivKey privKey, // add to kademlia routing table iffi // 1) we haven't already dialled them // 2) they accept a new kademlia stream - if (! existing.isEmpty()) + if (existing.isEmpty()) connection.muxerSession().createStream(wan.get()); } }); From 8b6dd6f54bcf69d1379754cf4f5d86bff641a1f0 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 5 Dec 2023 16:51:58 +0000 Subject: [PATCH 128/218] Don't identify incoming connections for peers already in address book --- src/main/java/org/peergos/HostBuilder.java | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/peergos/HostBuilder.java b/src/main/java/org/peergos/HostBuilder.java index a71054d0..88f3c0a1 100644 --- a/src/main/java/org/peergos/HostBuilder.java +++ b/src/main/java/org/peergos/HostBuilder.java @@ -184,16 +184,19 @@ public static Host build(PrivKey privKey, addrs.addAddrs(remotePeer, 0, remote); if (connection.isInitiator()) return; - StreamPromise stream = connection.muxerSession() - .createStream(new IdentifyBinding(new IdentifyProtocol())); - stream.getController() - .thenCompose(IdentifyController::id) - .thenAccept(remoteId -> { - Multiaddr[] remoteAddrs = remoteId.getListenAddrsList() - .stream() - .map(bytes -> Multiaddr.deserialize(bytes.toByteArray())) - .toArray(Multiaddr[]::new); - addrs.getAddrs(remotePeer).thenAccept(existing -> { + addrs.getAddrs(remotePeer).thenAccept(existing -> { + if (existing.isEmpty()) + return; + StreamPromise stream = connection.muxerSession() + .createStream(new IdentifyBinding(new IdentifyProtocol())); + stream.getController() + .thenCompose(IdentifyController::id) + .thenAccept(remoteId -> { + Multiaddr[] remoteAddrs = remoteId.getListenAddrsList() + .stream() + .map(bytes -> Multiaddr.deserialize(bytes.toByteArray())) + .toArray(Multiaddr[]::new); + addrs.addAddrs(remotePeer, 0, remoteAddrs); List protocolIds = remoteId.getProtocolsList().stream().collect(Collectors.toList()); if (protocolIds.contains(Kademlia.WAN_DHT_ID) && wan.isPresent()) { @@ -204,7 +207,7 @@ public static Host build(PrivKey privKey, connection.muxerSession().createStream(wan.get()); } }); - }); + }); }); for (String listenAddr : listenAddrs) { From 6ea7add56555c038279c89bf363fc3cb90e83a9e Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 5 Dec 2023 16:53:33 +0000 Subject: [PATCH 129/218] Fix prev commit --- src/main/java/org/peergos/HostBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/HostBuilder.java b/src/main/java/org/peergos/HostBuilder.java index 88f3c0a1..99be4047 100644 --- a/src/main/java/org/peergos/HostBuilder.java +++ b/src/main/java/org/peergos/HostBuilder.java @@ -185,7 +185,7 @@ public static Host build(PrivKey privKey, if (connection.isInitiator()) return; addrs.getAddrs(remotePeer).thenAccept(existing -> { - if (existing.isEmpty()) + if (! existing.isEmpty()) return; StreamPromise stream = connection.muxerSession() .createStream(new IdentifyBinding(new IdentifyProtocol())); From bbf6ad69ef6b33acf23d8d1ba584410e1d3683fc Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 6 Dec 2023 12:45:05 +0000 Subject: [PATCH 130/218] Only return public addresses in kademlia queries --- src/main/java/org/peergos/PeerAddresses.java | 11 +++++++ .../peergos/protocol/dht/KademliaEngine.java | 31 ++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/peergos/PeerAddresses.java b/src/main/java/org/peergos/PeerAddresses.java index 5954505e..f2afb379 100644 --- a/src/main/java/org/peergos/PeerAddresses.java +++ b/src/main/java/org/peergos/PeerAddresses.java @@ -13,6 +13,7 @@ import java.io.*; import java.net.*; import java.util.*; +import java.util.function.*; import java.util.stream.*; public class PeerAddresses { @@ -70,6 +71,16 @@ public Dht.Message.Peer toProtobuf() { .build(); } + public Dht.Message.Peer toProtobuf(Predicate filter) { + return Dht.Message.Peer.newBuilder() + .setId(ByteString.copyFrom(peerId.toBytes())) + .addAllAddrs(addresses.stream() + .filter(filter) + .map(a -> ByteString.copyFrom(a.serialize())) + .collect(Collectors.toList())) + .build(); + } + public static PeerAddresses fromHost(Host host) { Multihash peerId = Multihash.deserialize(host.getPeerId().getBytes()); List addrs = host.listenAddresses(); diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 68e5f59d..eaba4464 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -8,12 +8,15 @@ import io.libp2p.core.*; import io.libp2p.core.Stream; import io.libp2p.core.multiformats.*; +import io.libp2p.core.multiformats.Protocol; import io.prometheus.client.*; import org.peergos.*; import org.peergos.blockstore.*; import org.peergos.protocol.dht.pb.*; import org.peergos.protocol.ipns.*; +import java.io.*; +import java.net.*; import java.time.*; import java.util.*; import java.util.stream.*; @@ -117,7 +120,7 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { .setValue(ByteString.copyFrom(ipnsRecord.get().raw)).build()); builder = builder.addAllCloserPeers(getKClosestPeers(msg.getKey().toByteArray()) .stream() - .map(PeerAddresses::toProtobuf) + .map(p -> p.toProtobuf(a -> isPublic(a))) .collect(Collectors.toList())); Dht.Message reply = builder.build(); stream.writeAndFlush(reply); @@ -140,14 +143,17 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { if (blocks.hasAny(hash).join()) { providers = new HashSet<>(providers); providers.add(new PeerAddresses(ourPeerId, - new ArrayList<>(addressBook.getAddrs(PeerId.fromBase58(ourPeerId.toBase58())).join())).toProtobuf()); + addressBook.getAddrs(PeerId.fromBase58(ourPeerId.toBase58())).join() + .stream() + .filter(a -> isPublic(a)) + .collect(Collectors.toList())).toProtobuf()); } Dht.Message.Builder builder = msg.toBuilder(); builder = builder.addAllProviderPeers(providers.stream() .collect(Collectors.toList())); builder = builder.addAllCloserPeers(getKClosestPeers(msg.getKey().toByteArray()) .stream() - .map(PeerAddresses::toProtobuf) + .map(p -> p.toProtobuf(a -> isPublic(a))) .collect(Collectors.toList())); Dht.Message reply = builder.build(); stream.writeAndFlush(reply); @@ -162,7 +168,7 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { builder = builder.addAllCloserPeers(getKClosestPeers(target) .stream() .filter(p -> ! p.peerId.equals(sourcePeer)) // don't tell a peer about themselves - .map(PeerAddresses::toProtobuf) + .map(p -> p.toProtobuf(a -> isPublic(a))) .collect(Collectors.toList())); Dht.Message reply = builder.build(); stream.writeAndFlush(reply); @@ -174,4 +180,21 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { default: throw new IllegalStateException("Unknown message kademlia type: " + msg.getType()); } } + + public static boolean isPublic(Multiaddr addr) { + try { + List parts = addr.getComponents(); + for (MultiaddrComponent part: parts) { + if (part.getProtocol() == Protocol.IP6ZONE) + return true; + if (part.getProtocol() == Protocol.IP4 || part.getProtocol() == Protocol.IP6) { + InetAddress ip = InetAddress.getByName(part.getStringValue()); + if (ip.isLoopbackAddress() || ip.isSiteLocalAddress() || ip.isLinkLocalAddress() || ip.isAnyLocalAddress()) + return false; + return true; + } + } + } catch (IOException e) {} + return false; + } } From aec72ef3963b299252de5a56d269886487fe2ca2 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 11 Dec 2023 11:36:46 +0000 Subject: [PATCH 131/218] Generalise ipns publishing and resolution to arbitrary byte[] --- src/main/java/org/peergos/EmbeddedIpfs.java | 17 ++++++ .../protocol/dht/DatabaseRecordStore.java | 8 +-- .../org/peergos/protocol/dht/Kademlia.java | 52 ++++++++++++++----- .../protocol/dht/KademliaController.java | 6 +-- .../java/org/peergos/protocol/ipns/IPNS.java | 6 +-- .../org/peergos/protocol/ipns/IpnsRecord.java | 4 +- .../org/peergos/DatabaseRecordStoreTest.java | 2 +- .../java/org/peergos/EmbeddedIpfsTest.java | 15 ++++++ src/test/java/org/peergos/IpnsTest.java | 2 +- 9 files changed, 86 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index e0cb0541..ec446661 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -4,6 +4,7 @@ import io.ipfs.multiaddr.*; import io.ipfs.multihash.Multihash; import io.libp2p.core.*; +import io.libp2p.core.crypto.*; import io.libp2p.core.multiformats.*; import io.libp2p.core.multistream.*; import io.libp2p.protocol.*; @@ -28,6 +29,7 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; +import java.time.*; import java.util.*; import java.util.concurrent.*; import java.util.logging.*; @@ -98,6 +100,21 @@ public List getBlocks(List wants, Set peers, boolean .collect(Collectors.toList()); } + public CompletableFuture publishValue(PrivKey priv, byte[] value, long sequence) { + Multihash pub = Multihash.deserialize(PeerId.fromPubKey(priv.publicKey()).getBytes()); + int hours = 1; + LocalDateTime expiry = LocalDateTime.now().plusHours(hours); + long ttlNanos = hours * 3600_000_000_000L; + return dht.publishValue(priv, pub, value, sequence, expiry, ttlNanos, node); + } + + public CompletableFuture resolveValue(PubKey pub) { + Multihash publisher = Multihash.deserialize(PeerId.fromPubKey(pub).getBytes()); + CompletableFuture res = new CompletableFuture<>(); + dht.resolveValue(publisher, node, Kademlia.getNRecords(2, res)); + return res; + } + public void start() { LOG.info("Starting IPFS..."); Thread shutdownHook = new Thread(() -> { diff --git a/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java b/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java index b5e65930..16447c4b 100644 --- a/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java +++ b/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java @@ -10,7 +10,7 @@ import java.sql.*; import java.time.LocalDateTime; import java.time.ZoneOffset; -import java.util.Optional; +import java.util.*; public class DatabaseRecordStore implements RecordStore { @@ -76,7 +76,7 @@ public Optional get(Cid peerId) { LocalDateTime expiry = LocalDateTime.ofEpochSecond(rs.getLong("expiryUTC"), 0, ZoneOffset.UTC); IpnsRecord record = new IpnsRecord(bout.toByteArray(), rs.getLong("sequence"), - rs.getLong("ttlNanos"), expiry, rs.getString("val")); + rs.getLong("ttlNanos"), expiry, rs.getString("val").getBytes()); return Optional.of(record); } catch (IOException readEx) { throw new IllegalStateException(readEx); @@ -102,8 +102,8 @@ public void put(Multihash peerId, IpnsRecord record) { pstmt.setLong(3, record.sequence); pstmt.setLong(4, record.ttlNanos); pstmt.setLong(5, record.expiry.toEpochSecond(ZoneOffset.UTC)); - pstmt.setString(6, record.value.length() > SIZE_OF_VAL ? - record.value.substring(0, SIZE_OF_VAL) : record.value); + pstmt.setString(6, new String(record.value.length > SIZE_OF_VAL ? + Arrays.copyOfRange(record.value, 0, SIZE_OF_VAL) : record.value)); pstmt.executeUpdate(); } catch (SQLException ex) { throw new IllegalStateException(ex); diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 47e13716..482701a5 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -297,19 +297,33 @@ public CompletableFuture provideBlock(Multihash block, Host us, PeerAddres return CompletableFuture.allOf(provides.toArray(new CompletableFuture[0])); } - public CompletableFuture publishIpnsValue(PrivKey priv, Multihash publisher, Multihash value, long sequence, Host us) { + public CompletableFuture publishIpnsValue(PrivKey priv, + Multihash publisher, + Multihash value, + long sequence, + Host us) { int hours = 1; LocalDateTime expiry = LocalDateTime.now().plusHours(hours); - long ttl = hours * 3600_000_000_000L; + long ttlNanos = hours * 3600_000_000_000L; + byte[] publishValue = ("/ipfs/" + value).getBytes(); + return publishValue(priv, publisher, publishValue, sequence, expiry, ttlNanos, us); + } + public CompletableFuture publishValue(PrivKey priv, + Multihash publisher, + byte[] publishValue, + long sequence, + LocalDateTime expiry, + long ttlNanos, + Host us) { int publishes = 0; for (int i=0; i < 5 && publishes < 20; i++) { List closestPeers = findClosestPeers(publisher, 25, us); publishes += closestPeers.stream().parallel().mapToInt(peer -> { try { boolean success = dialPeer(peer, us).join() - .putValue("/ipfs/" + value, expiry, sequence, - ttl, publisher, priv).join(); + .putValue(publishValue, expiry, sequence, + ttlNanos, publisher, priv).join(); if (success) return 1; } catch (Exception e) {} @@ -319,9 +333,28 @@ public CompletableFuture publishIpnsValue(PrivKey priv, Multihash publishe return CompletableFuture.completedFuture(null); } + public static Predicate getNRecords(int minResults, CompletableFuture res) { + List candidates = new ArrayList<>(); + return rec -> { + candidates.add(rec); + if (candidates.size() >= minResults) { + // Validate and sort records by sequence number + List records = candidates.stream().sorted().collect(Collectors.toList()); + res.complete(records.get(records.size() - 1).value); + return false; + } + return true; + }; + } + public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, int minResults) { + CompletableFuture res = new CompletableFuture<>(); + resolveValue(publisher, us, getNRecords(minResults, res)); + return res.thenApply(String::new); + } + + public void resolveValue(Multihash publisher, Host us, Predicate getMore) { List closestPeers = findClosestPeers(publisher, 20, us); - List candidates = new ArrayList<>(); Set queryCandidates = new HashSet<>(); Set queriedPeers = new HashSet<>(); for (PeerAddresses peer : closestPeers) { @@ -331,15 +364,10 @@ public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, try { GetResult res = dialPeer(peer, us).join().getValue(publisher).join(); if (res.record.isPresent() && res.record.get().publisher.equals(publisher)) - candidates.add(res.record.get().value); + if ( !getMore.test(res.record.get().value)) + return; queryCandidates.addAll(res.closerPeers); } catch (Exception e) {} - if (candidates.size() >= minResults) - break; } - - // Validate and sort records by sequence number - List records = candidates.stream().sorted().collect(Collectors.toList()); - return CompletableFuture.completedFuture(records.get(records.size() - 1).value); } } diff --git a/src/main/java/org/peergos/protocol/dht/KademliaController.java b/src/main/java/org/peergos/protocol/dht/KademliaController.java index da17a78f..cbff1672 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaController.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaController.java @@ -47,9 +47,9 @@ default CompletableFuture getProviders(Multihash block) { .thenApply(Providers::fromProtobuf); } - default CompletableFuture putValue(String pathToPublish, LocalDateTime expiry, long sequence, + default CompletableFuture putValue(byte[] value, LocalDateTime expiry, long sequence, long ttlNanos, Multihash peerId, PrivKey ourKey) { - byte[] cborEntryData = IPNS.createCborDataForIpnsEntry(pathToPublish, expiry, + byte[] cborEntryData = IPNS.createCborDataForIpnsEntry(value, expiry, Ipns.IpnsEntry.ValidityType.EOL_VALUE, sequence, ttlNanos); String expiryString = IPNS.formatExpiry(expiry); byte[] signature = ourKey.sign(IPNS.createSigV2Data(cborEntryData)); @@ -62,7 +62,7 @@ default CompletableFuture putValue(String pathToPublish, LocalDateTime byte[] ipnsEntry = Ipns.IpnsEntry.newBuilder() .setSequence(sequence) .setTtl(ttlNanos) - .setValue(ByteString.copyFrom(pathToPublish.getBytes())) + .setValue(ByteString.copyFrom(value)) .setValidityType(Ipns.IpnsEntry.ValidityType.EOL) .setValidity(ByteString.copyFrom(expiryString.getBytes())) .setData(ByteString.copyFrom(cborEntryData)) diff --git a/src/main/java/org/peergos/protocol/ipns/IPNS.java b/src/main/java/org/peergos/protocol/ipns/IPNS.java index 28ea5c13..044a6a56 100644 --- a/src/main/java/org/peergos/protocol/ipns/IPNS.java +++ b/src/main/java/org/peergos/protocol/ipns/IPNS.java @@ -40,14 +40,14 @@ public static Cid getCidFromKey(ByteString key) { return Cid.cast(key.substring(6).toByteArray()); } - public static byte[] createCborDataForIpnsEntry(String pathToPublish, + public static byte[] createCborDataForIpnsEntry(byte[] value, LocalDateTime expiry, long validityType, long sequence, long ttl) { SortedMap state = new TreeMap<>(); state.put("TTL", new CborObject.CborLong(ttl)); - state.put("Value", new CborObject.CborByteArray(pathToPublish.getBytes())); + state.put("Value", new CborObject.CborByteArray(value)); state.put("Sequence", new CborObject.CborLong(sequence)); String expiryString = formatExpiry(expiry); state.put("Validity", new CborObject.CborByteArray(expiryString.getBytes(StandardCharsets.UTF_8))); @@ -109,7 +109,7 @@ public static Optional validateIpnsEntry(Dht.Message msg) { if (expiry.isBefore(LocalDateTime.now())) return Optional.empty(); byte[] entryBytes = msg.getRecord().getValue().toByteArray(); - IpnsRecord record = new IpnsRecord(entryBytes, entry.getSequence(), entry.getTtl(), expiry, entry.getValue().toStringUtf8()); + IpnsRecord record = new IpnsRecord(entryBytes, entry.getSequence(), entry.getTtl(), expiry, entry.getValue().toByteArray()); return Optional.of(new IpnsMapping(signer, record)); } catch (InvalidProtocolBufferException e) { return Optional.empty(); diff --git a/src/main/java/org/peergos/protocol/ipns/IpnsRecord.java b/src/main/java/org/peergos/protocol/ipns/IpnsRecord.java index 44df90e4..5db16e34 100644 --- a/src/main/java/org/peergos/protocol/ipns/IpnsRecord.java +++ b/src/main/java/org/peergos/protocol/ipns/IpnsRecord.java @@ -9,9 +9,9 @@ public class IpnsRecord implements Comparable { public final byte[] raw; public final long sequence, ttlNanos; public final LocalDateTime expiry; - public final String value; + public final byte[] value; - public IpnsRecord(byte[] raw, long sequence, long ttlNanos, LocalDateTime expiry, String value) { + public IpnsRecord(byte[] raw, long sequence, long ttlNanos, LocalDateTime expiry, byte[] value) { this.raw = raw; this.sequence = sequence; this.ttlNanos = ttlNanos; diff --git a/src/test/java/org/peergos/DatabaseRecordStoreTest.java b/src/test/java/org/peergos/DatabaseRecordStoreTest.java index 23fed0da..0bc80d52 100644 --- a/src/test/java/org/peergos/DatabaseRecordStoreTest.java +++ b/src/test/java/org/peergos/DatabaseRecordStoreTest.java @@ -13,7 +13,7 @@ public class DatabaseRecordStoreTest { public void testRecordStore() { try (DatabaseRecordStore bs = new DatabaseRecordStore("mem:")) { LocalDateTime now = LocalDateTime.now(); - IpnsRecord record = new IpnsRecord("raw".getBytes(), 1, 2, now, "value"); + IpnsRecord record = new IpnsRecord("raw".getBytes(), 1, 2, now, "value".getBytes()); Cid peerId = Cid.decode("zb2rhYSxw4ZjuzgCnWSt19Q94ERaeFhu9uSqRgjSdx9bsgM6f"); bs.put(peerId, record); //make sure PUTing a second time succeeds diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 8aa4b356..bb929b29 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -6,6 +6,7 @@ import io.libp2p.core.*; import io.libp2p.core.crypto.*; import io.libp2p.core.multiformats.*; +import io.libp2p.crypto.keys.*; import io.libp2p.protocol.*; import org.junit.*; import org.peergos.blockstore.*; @@ -39,6 +40,20 @@ public void largeBlock() throws Exception { node2.stop(); } + @Test + public void publishValue() throws Exception { + EmbeddedIpfs node1 = build(BootstrapTest.BOOTSTRAP_NODES, List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); + node1.start(); + + PrivKey publisher = Ed25519Kt.generateEd25519KeyPair().getFirst(); + byte[] value = "This is a test".getBytes(); + node1.publishValue(publisher, value, 1).join(); + byte[] res = node1.resolveValue(publisher.publicKey()).join(); + Assert.assertTrue(Arrays.equals(res, value)); + + node1.stop(); + } + @Test public void wildcardListenerAddressesGetExpanded() { int node1Port = TestPorts.getPort(); diff --git a/src/test/java/org/peergos/IpnsTest.java b/src/test/java/org/peergos/IpnsTest.java index f917cde6..8ea57bff 100644 --- a/src/test/java/org/peergos/IpnsTest.java +++ b/src/test/java/org/peergos/IpnsTest.java @@ -49,7 +49,7 @@ public void publishIPNSRecordToKubo() throws IOException { for (int i = 0; i < 10; i++) { try { success = dht.dial(node1, address2).getController().join() - .putValue(pathToPublish, expiry, sequence, ttl, node1Id, node1.getPrivKey()) + .putValue(pathToPublish.getBytes(), expiry, sequence, ttl, node1Id, node1.getPrivKey()) .orTimeout(2, TimeUnit.SECONDS).join(); break; } catch (Exception timeout) { From 71be5c4be11bc8562265a2225b5d21a6675356f9 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 11 Dec 2023 14:36:06 +0000 Subject: [PATCH 132/218] Improve test logging for ipns benchmark --- src/test/java/org/peergos/KademliaTest.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 96ad00c2..2b0c70f5 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -96,7 +96,7 @@ public void ipnsBenchmark() throws Exception { long p0 = System.currentTimeMillis(); dht1.publishIpnsValue(signer, pub, value, 1, node1).join(); long p1 = System.currentTimeMillis(); - System.out.println("Publish took " + (p1-p0) + "ms"); + System.out.println("Publish took " + printSeconds(p1-p0) + "s"); publishTotal += p1-p0; // retrieve it from node 2 @@ -104,7 +104,7 @@ public void ipnsBenchmark() throws Exception { String res = dht2.resolveIpnsValue(pub, node2, 1).orTimeout(10, TimeUnit.SECONDS).join(); long t1 = System.currentTimeMillis(); Assert.assertTrue(res.equals("/ipfs/" + value)); - System.out.println("Resolved in " + (t1 - t0) + "ms"); + System.out.println("Resolved in " + printSeconds(t1 - t0) + "s"); resolveTotal += t1-t0; } System.out.println("Publish av: " + publishTotal/iterations + ", resolve av: " + resolveTotal/iterations); @@ -116,7 +116,7 @@ public void ipnsBenchmark() throws Exception { String res = dht2.resolveIpnsValue(pub, node2, 1).orTimeout(10, TimeUnit.SECONDS).join(); long t1 = System.currentTimeMillis(); Assert.assertTrue(res.equals("/ipfs/" + value)); - System.out.println("Resolved again in " + (t1 - t0) + "ms"); + System.out.println("Resolved again in " + printSeconds(t1 - t0) + "s"); } } finally { node1.stop(); @@ -124,6 +124,10 @@ public void ipnsBenchmark() throws Exception { } } + public static String printSeconds(long millis) { + return millis / 1000 + "." + (millis % 1000)/100; + } + @Test public void kademliaFindNodeLimitTest() { PeerId us = new HostBuilder().generateIdentity().getPeerId(); From f30a124db56e68a20fa2013911c57c03469ae3cf Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 11 Dec 2023 15:24:48 +0000 Subject: [PATCH 133/218] Make ipns publishing 2X faster (45s down to 15s) --- .../org/peergos/protocol/dht/Kademlia.java | 31 ++++++++++--------- src/test/java/org/peergos/KademliaTest.java | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 482701a5..dd392c6a 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -133,6 +133,8 @@ private int compareKeys(RoutingEntry a, RoutingEntry b, Id keyId) { return a.addresses.peerId.toBase58().compareTo(b.addresses.peerId.toBase58()); } + private final ExecutorService ioExec = Executors.newFixedThreadPool(16); + public List findClosestPeers(Multihash peerIdkey, int maxCount, Host us) { byte[] key = peerIdkey.toBytes(); Id keyId = Id.create(Hash.sha256(key), 256); @@ -154,19 +156,20 @@ public List findClosestPeers(Multihash peerIdkey, int maxCount, H Set queried = Collections.synchronizedSet(new HashSet<>()); int queryParallelism = 3; while (true) { - List>> futures = toQuery.stream() + List thisRound = toQuery.stream() .limit(queryParallelism) - .parallel() + .collect(Collectors.toList()); + List>> futures = thisRound.stream() .map(r -> { toQuery.remove(r); queried.add(r.addresses.peerId); - return getCloserPeers(peerIdkey, r.addresses, us); + return ioExec.submit(() -> getCloserPeers(peerIdkey, r.addresses, us).join()); }) .collect(Collectors.toList()); boolean foundCloser = false; - for (CompletableFuture> future : futures) { + for (Future> future : futures) { try { - List result = future.join(); + List result = future.get(); for (PeerAddresses peer : result) { if (!queried.contains(peer.peerId)) { // exit early if we are looking for the specific node @@ -316,19 +319,19 @@ public CompletableFuture publishValue(PrivKey priv, LocalDateTime expiry, long ttlNanos, Host us) { - int publishes = 0; - for (int i=0; i < 5 && publishes < 20; i++) { + Set publishes = Collections.synchronizedSet(new HashSet<>()); + for (int i=0; i < 5 && publishes.size() < 20; i++) { List closestPeers = findClosestPeers(publisher, 25, us); - publishes += closestPeers.stream().parallel().mapToInt(peer -> { + closestPeers.forEach(peer -> ioExec.submit(() -> { try { - boolean success = dialPeer(peer, us).join() + dialPeer(peer, us).join() .putValue(publishValue, expiry, sequence, - ttlNanos, publisher, priv).join(); - if (success) - return 1; + ttlNanos, publisher, priv).thenAccept(success -> { + if (success) + publishes.add(peer.peerId); + }); } catch (Exception e) {} - return 0; - }).sum(); + })); } return CompletableFuture.completedFuture(null); } diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 2b0c70f5..0b2a309f 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -87,7 +87,7 @@ public void ipnsBenchmark() throws Exception { List signers = new ArrayList<>(); long publishTotal = 0, resolveTotal = 0; - int iterations = 10; + int iterations = 5; for (int i = 0; i < iterations; i++) { // publish mapping from node 1 PrivKey signer = Ed25519Kt.generateEd25519KeyPair().getFirst(); From 90a708b6711d88ca179026a6ef0c578c49d96c8d Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 12 Dec 2023 10:50:50 +0000 Subject: [PATCH 134/218] Generalise kademlia.closerPeers to arbitrary keys Make sure we iclude /ipns/ in key wqhen publishing a value Use GET_VALUE calls only when looking up a value (using returned closer peers to iterate) Most IPNS lookups are now < 1s --- src/main/java/org/peergos/EmbeddedIpfs.java | 7 +- .../org/peergos/protocol/dht/Kademlia.java | 139 +++++++++++------- .../protocol/dht/KademliaController.java | 4 +- src/test/java/org/peergos/FindPeerTest.java | 2 +- 4 files changed, 95 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index ec446661..0af8a9ec 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -23,6 +23,7 @@ import org.peergos.protocol.circuit.*; import org.peergos.protocol.dht.*; import org.peergos.protocol.http.*; +import org.peergos.protocol.ipns.*; import org.peergos.util.Logging; import java.nio.file.*; @@ -110,9 +111,9 @@ public CompletableFuture publishValue(PrivKey priv, byte[] value, long seq public CompletableFuture resolveValue(PubKey pub) { Multihash publisher = Multihash.deserialize(PeerId.fromPubKey(pub).getBytes()); - CompletableFuture res = new CompletableFuture<>(); - dht.resolveValue(publisher, node, Kademlia.getNRecords(2, res)); - return res; + List candidates = dht.resolveValue(publisher, 1, node); + List records = candidates.stream().sorted().collect(Collectors.toList()); + return CompletableFuture.completedFuture(records.get(records.size() - 1).value); } public void start() { diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index dd392c6a..422a24d6 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -136,16 +136,21 @@ private int compareKeys(RoutingEntry a, RoutingEntry b, Id keyId) { private final ExecutorService ioExec = Executors.newFixedThreadPool(16); public List findClosestPeers(Multihash peerIdkey, int maxCount, Host us) { + if (maxCount == 1) { + Collection existing = addressBook.get(PeerId.fromBase58(peerIdkey.toBase58())).join(); + if (!existing.isEmpty()) + return Collections.singletonList(new PeerAddresses(peerIdkey, new ArrayList<>(existing))); + } byte[] key = peerIdkey.toBytes(); + return findClosestPeers(key, maxCount, us); + } + public List findClosestPeers(byte[] key, int maxCount, Host us) { Id keyId = Id.create(Hash.sha256(key), 256); SortedSet closest = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); List localClosest = engine.getKClosestPeers(key); if (maxCount == 1) { - Collection existing = addressBook.get(PeerId.fromBase58(peerIdkey.toBase58())).join(); - if (! existing.isEmpty()) - return Collections.singletonList(new PeerAddresses(peerIdkey, new ArrayList<>(existing))); - Optional match = localClosest.stream().filter(p -> p.peerId.equals(peerIdkey)).findFirst(); + Optional match = localClosest.stream().filter(p -> Arrays.equals(p.peerId.toBytes(), key)).findFirst(); if (match.isPresent()) return Collections.singletonList(match.get()); } @@ -163,7 +168,7 @@ public List findClosestPeers(Multihash peerIdkey, int maxCount, H .map(r -> { toQuery.remove(r); queried.add(r.addresses.peerId); - return ioExec.submit(() -> getCloserPeers(peerIdkey, r.addresses, us).join()); + return ioExec.submit(() -> getCloserPeers(key, r.addresses, us).join()); }) .collect(Collectors.toList()); boolean foundCloser = false; @@ -173,7 +178,7 @@ public List findClosestPeers(Multihash peerIdkey, int maxCount, H for (PeerAddresses peer : result) { if (!queried.contains(peer.peerId)) { // exit early if we are looking for the specific node - if (maxCount == 1 && peer.peerId.equals(peerIdkey)) + if (maxCount == 1 && Arrays.equals(peer.peerId.toBytes(), key)) return Collections.singletonList(peer); queried.add(peer.peerId); Id peerKey = Id.create(Hash.sha256(peer.peerId.toBytes()), 256); @@ -253,17 +258,17 @@ public CompletableFuture> findProviders(Multihash block, Hos return CompletableFuture.completedFuture(providers); } - private CompletableFuture> getCloserPeers(Multihash peerIDKey, PeerAddresses target, Host us) { + private CompletableFuture> getCloserPeers(byte[] key, PeerAddresses target, Host us) { try { - return dialPeer(target, us).orTimeout(2, TimeUnit.SECONDS).join().closerPeers(peerIDKey); + return dialPeer(target, us).orTimeout(2, TimeUnit.SECONDS).join().closerPeers(key); } catch (Exception e) { // we can't dial quic only nodes until it's implemented if (target.addresses.stream().allMatch(a -> a.toString().contains("quic"))) return CompletableFuture.completedFuture(Collections.emptyList()); if (e.getCause() instanceof NothingToCompleteException || e.getCause() instanceof NonCompleteException) { - LOG.fine("Couldn't dial " + peerIDKey + " addrs: " + target.addresses); + LOG.fine("Couldn't dial " + target.peerId + " addrs: " + target.addresses); } else if (e.getCause() instanceof TimeoutException) - LOG.fine("Timeout dialing " + peerIDKey + " addrs: " + target.addresses); + LOG.fine("Timeout dialing " + target.peerId + " addrs: " + target.addresses); else if (e.getCause() instanceof ConnectionClosedException) {} else e.printStackTrace(); @@ -321,56 +326,88 @@ public CompletableFuture publishValue(PrivKey priv, Host us) { Set publishes = Collections.synchronizedSet(new HashSet<>()); for (int i=0; i < 5 && publishes.size() < 20; i++) { - List closestPeers = findClosestPeers(publisher, 25, us); - closestPeers.forEach(peer -> ioExec.submit(() -> { + List closestPeers = findClosestPeers(IPNS.getKey(publisher), 25, us); + List> futures = closestPeers.stream() + .filter(p -> !publishes.contains(p.peerId)) + .map(peer -> ioExec.submit(() -> { + try { + dialPeer(peer, us).join() + .putValue(publishValue, expiry, sequence, + ttlNanos, publisher, priv).thenAccept(success -> { + if (success) + publishes.add(peer.peerId); + }); + } catch (Exception e) {} + })).collect(Collectors.toList()); + futures.forEach(f -> { try { - dialPeer(peer, us).join() - .putValue(publishValue, expiry, sequence, - ttlNanos, publisher, priv).thenAccept(success -> { - if (success) - publishes.add(peer.peerId); - }); + f.get(); } catch (Exception e) {} - })); + }); } return CompletableFuture.completedFuture(null); } - public static Predicate getNRecords(int minResults, CompletableFuture res) { - List candidates = new ArrayList<>(); - return rec -> { - candidates.add(rec); - if (candidates.size() >= minResults) { - // Validate and sort records by sequence number - List records = candidates.stream().sorted().collect(Collectors.toList()); - res.complete(records.get(records.size() - 1).value); - return false; - } - return true; - }; - } - public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, int minResults) { - CompletableFuture res = new CompletableFuture<>(); - resolveValue(publisher, us, getNRecords(minResults, res)); - return res.thenApply(String::new); + List candidates = resolveValue(publisher, minResults, us); + List records = candidates.stream().sorted().collect(Collectors.toList()); + if (records.isEmpty()) + return CompletableFuture.failedFuture(new IllegalStateException("Couldn't find IPNS value for " + publisher)); + return CompletableFuture.completedFuture(new String(records.get(records.size() - 1).value)); } - public void resolveValue(Multihash publisher, Host us, Predicate getMore) { - List closestPeers = findClosestPeers(publisher, 20, us); - Set queryCandidates = new HashSet<>(); - Set queriedPeers = new HashSet<>(); - for (PeerAddresses peer : closestPeers) { - if (queriedPeers.contains(peer.peerId)) - continue; - queriedPeers.add(peer.peerId); - try { - GetResult res = dialPeer(peer, us).join().getValue(publisher).join(); - if (res.record.isPresent() && res.record.get().publisher.equals(publisher)) - if ( !getMore.test(res.record.get().value)) - return; - queryCandidates.addAll(res.closerPeers); - } catch (Exception e) {} + private Optional getValueFromPeer(PeerAddresses peer, Multihash publisher, Host us) { + try { + return Optional.of(dialPeer(peer, us).join().getValue(publisher).join()); + } catch (Exception e) {} + return Optional.empty(); + } + public List resolveValue(Multihash publisher, int minResults, Host us) { + byte[] key = IPNS.getKey(publisher); + List candidates = new ArrayList<>(); + Id keyId = Id.create(Hash.sha256(key), 256); + SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); + List localClosest = engine.getKClosestPeers(key); + int queryParallelism = 3; + toQuery.addAll(localClosest.stream() + .limit(queryParallelism) + .map(p -> new RoutingEntry(Id.create(Hash.sha256(p.peerId.toBytes()), 256), p)) + .collect(Collectors.toList())); + Set queried = Collections.synchronizedSet(new HashSet<>()); + while (! toQuery.isEmpty()) { + int remaining = toQuery.size() - 3; + List thisRound = toQuery.stream() + .limit(queryParallelism) + .collect(Collectors.toList()); + List> futures = thisRound.stream() + .map(r -> { + toQuery.remove(r); + queried.add(r.addresses.peerId); + return ioExec.submit(() -> getValueFromPeer(r.addresses, publisher, us) + .ifPresent(g -> { + if (g.record.isPresent() && g.record.get().publisher.equals(publisher)) + candidates.add(g.record.get().value); + for (PeerAddresses peer : g.closerPeers) { + if (! queried.contains(peer.peerId)) { + Id peerKey = Id.create(Hash.sha256(IPNS.getKey(peer.peerId)), 256); + RoutingEntry e = new RoutingEntry(peerKey, peer); + toQuery.add(e); + } + } + })); + }) + .collect(Collectors.toList()); + futures.forEach(f -> { + try { + f.get(); + } catch (Exception e) {} + }); + // exit early if we have enough results + if (candidates.size() >= minResults) + break; + if (toQuery.size() == remaining) + break; } + return candidates; } } diff --git a/src/main/java/org/peergos/protocol/dht/KademliaController.java b/src/main/java/org/peergos/protocol/dht/KademliaController.java index cbff1672..e5f6aa99 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaController.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaController.java @@ -20,10 +20,10 @@ public interface KademliaController { CompletableFuture send(Dht.Message msg); - default CompletableFuture> closerPeers(Multihash peerID) { + default CompletableFuture> closerPeers(byte[] key) { return rpc(Dht.Message.newBuilder() .setType(Dht.Message.MessageType.FIND_NODE) - .setKey(ByteString.copyFrom(peerID.toBytes())) + .setKey(ByteString.copyFrom(key)) .build()) .thenApply(resp -> resp.getCloserPeersList().stream() .map(PeerAddresses::fromProtobuf) diff --git a/src/test/java/org/peergos/FindPeerTest.java b/src/test/java/org/peergos/FindPeerTest.java index 91c1610a..51173c18 100644 --- a/src/test/java/org/peergos/FindPeerTest.java +++ b/src/test/java/org/peergos/FindPeerTest.java @@ -49,7 +49,7 @@ private static long findAndDialPeer(Multihash toFind, Kademlia dht1, Host node1) PeerAddresses peer = matching.get(); Multiaddr[] addrs = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); dht1.dial(node1, PeerId.fromBase58(peer.peerId.toBase58()), addrs) - .getController().join().closerPeers(toFind).join(); + .getController().join().closerPeers(toFind.toBytes()).join(); System.out.println("Peer lookup took " + (t2-t1) + "ms"); return t2 - t1; } From 6117454eee5d96a75bc5b87ec885c0e07fb9e5b1 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 11:53:57 +0000 Subject: [PATCH 135/218] Reduce average IPNS publish time to ~6s --- .../org/peergos/protocol/dht/Kademlia.java | 70 +++++++++++++++---- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 422a24d6..02af9e05 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -317,6 +317,22 @@ public CompletableFuture publishIpnsValue(PrivKey priv, return publishValue(priv, publisher, publishValue, sequence, expiry, ttlNanos, us); } + private boolean putValue(PrivKey priv, + Multihash publisher, + byte[] publishValue, + long sequence, + LocalDateTime expiry, + long ttlNanos, + PeerAddresses peer, + Host us) { + try { + return dialPeer(peer, us).join() + .putValue(publishValue, expiry, sequence, + ttlNanos, publisher, priv).join(); + } catch (Exception e) {} + return false; + } + public CompletableFuture publishValue(PrivKey priv, Multihash publisher, byte[] publishValue, @@ -325,25 +341,53 @@ public CompletableFuture publishValue(PrivKey priv, long ttlNanos, Host us) { Set publishes = Collections.synchronizedSet(new HashSet<>()); - for (int i=0; i < 5 && publishes.size() < 20; i++) { - List closestPeers = findClosestPeers(IPNS.getKey(publisher), 25, us); - List> futures = closestPeers.stream() - .filter(p -> !publishes.contains(p.peerId)) - .map(peer -> ioExec.submit(() -> { - try { - dialPeer(peer, us).join() - .putValue(publishValue, expiry, sequence, - ttlNanos, publisher, priv).thenAccept(success -> { - if (success) - publishes.add(peer.peerId); + int minPublishes = 20; + + byte[] key = IPNS.getKey(publisher); + Id keyId = Id.create(Hash.sha256(key), 256); + SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); + List localClosest = engine.getKClosestPeers(key); + int queryParallelism = 3; + toQuery.addAll(localClosest.stream() + .limit(queryParallelism) + .map(p -> new RoutingEntry(Id.create(Hash.sha256(p.peerId.toBytes()), 256), p)) + .collect(Collectors.toList())); + Set queried = Collections.synchronizedSet(new HashSet<>()); + while (! toQuery.isEmpty()) { + int remaining = toQuery.size() - 3; + List thisRound = toQuery.stream() + .limit(queryParallelism) + .collect(Collectors.toList()); + List> futures = thisRound.stream() + .map(r -> { + toQuery.remove(r); + queried.add(r.addresses.peerId); + return ioExec.submit(() -> getCloserPeers(key, r.addresses, us).thenApply(res -> { + for (PeerAddresses peer : res) { + if (! queried.contains(peer.peerId)) { + Id peerKey = Id.create(Hash.sha256(IPNS.getKey(peer.peerId)), 256); + RoutingEntry e = new RoutingEntry(peerKey, peer); + toQuery.add(e); + } + } + ioExec.submit(() -> { + if (putValue(priv, publisher, publishValue, sequence, expiry, ttlNanos, r.addresses, us)) + publishes.add(r.addresses.peerId); }); - } catch (Exception e) {} - })).collect(Collectors.toList()); + return true; + })); + }) + .collect(Collectors.toList()); futures.forEach(f -> { try { f.get(); } catch (Exception e) {} }); + // exit early if we have enough results + if (publishes.size() >= minPublishes) + break; + if (toQuery.size() == remaining) + break; } return CompletableFuture.completedFuture(null); } From e1ccbfd8af1e2389591bf58c3b4352fcf7a0a868 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 12:01:30 +0000 Subject: [PATCH 136/218] Improve routing table bootstrap parallelism --- src/main/java/org/peergos/protocol/dht/Kademlia.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 02af9e05..b5ee46c9 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -55,18 +55,17 @@ public int bootstrapRoutingTable(Host host, List addrs, Predicate< }) .filter(filter) .collect(Collectors.toList()); - List> futures = resolved.stream() - .parallel() + List> futures = resolved.stream() .map(addr -> { Multiaddr addrWithPeer = Multiaddr.fromString(addr); addressBook.setAddrs(addrWithPeer.getPeerId(), 0, addrWithPeer); - return dial(host, addrWithPeer).getController(); + return ioExec.submit(() -> dial(host, addrWithPeer).getController().join()); }) .collect(Collectors.toList()); int successes = 0; - for (CompletableFuture future : futures) { + for (Future future : futures) { try { - future.orTimeout(5, TimeUnit.SECONDS).join(); + future.get(5, TimeUnit.SECONDS); successes++; } catch (Exception e) {} } From c8bef93d8ea138c7917b333a5bebee5a98311b4e Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 12:14:33 +0000 Subject: [PATCH 137/218] Improve error message --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 0af8a9ec..4cfd8646 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -113,6 +113,8 @@ public CompletableFuture resolveValue(PubKey pub) { Multihash publisher = Multihash.deserialize(PeerId.fromPubKey(pub).getBytes()); List candidates = dht.resolveValue(publisher, 1, node); List records = candidates.stream().sorted().collect(Collectors.toList()); + if (records.isEmpty()) + return CompletableFuture.failedFuture(new IllegalStateException("Couldn't resolve IPNS value for " + pub)); return CompletableFuture.completedFuture(records.get(records.size() - 1).value); } From 8b86b550dd8fbe88023e02b4e13b0022eaefbeb7 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 13:09:05 +0000 Subject: [PATCH 138/218] Improve publish quality This slows down publishing to 11s (a large fraction of closest nodes may be quic only, so we need to try next closest) --- .../org/peergos/protocol/dht/Kademlia.java | 24 ++++++++++++++++++- src/test/java/org/peergos/KademliaTest.java | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index b5ee46c9..85b850a3 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -385,8 +385,30 @@ public CompletableFuture publishValue(PrivKey priv, // exit early if we have enough results if (publishes.size() >= minPublishes) break; - if (toQuery.size() == remaining) + if (toQuery.size() == remaining) { + // publish to closest remaining nodes + while (publishes.size() < minPublishes) { + List closest = toQuery.stream() + .limit(minPublishes - publishes.size() + 5) + .collect(Collectors.toList()); + List> lastFutures = closest.stream() + .map(r -> { + toQuery.remove(r); + queried.add(r.addresses.peerId); + return ioExec.submit(() -> { + if (putValue(priv, publisher, publishValue, sequence, expiry, ttlNanos, r.addresses, us)) + publishes.add(r.addresses.peerId); + }); + }) + .collect(Collectors.toList()); + lastFutures.forEach(f -> { + try { + f.get(); + } catch (Exception e) {} + }); + } break; + } } return CompletableFuture.completedFuture(null); } diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 0b2a309f..b9517cc2 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -87,7 +87,7 @@ public void ipnsBenchmark() throws Exception { List signers = new ArrayList<>(); long publishTotal = 0, resolveTotal = 0; - int iterations = 5; + int iterations = 25; for (int i = 0; i < iterations; i++) { // publish mapping from node 1 PrivKey signer = Ed25519Kt.generateEd25519KeyPair().getFirst(); From 4bba11792e89548069d15019e8ad8e8585eca2af Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 13:11:42 +0000 Subject: [PATCH 139/218] Make the hours TTL of an IPNS record part of the API --- src/main/java/org/peergos/EmbeddedIpfs.java | 7 +++---- src/test/java/org/peergos/EmbeddedIpfsTest.java | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 4cfd8646..234f5661 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -101,11 +101,10 @@ public List getBlocks(List wants, Set peers, boolean .collect(Collectors.toList()); } - public CompletableFuture publishValue(PrivKey priv, byte[] value, long sequence) { + public CompletableFuture publishValue(PrivKey priv, byte[] value, long sequence, int hoursTtl) { Multihash pub = Multihash.deserialize(PeerId.fromPubKey(priv.publicKey()).getBytes()); - int hours = 1; - LocalDateTime expiry = LocalDateTime.now().plusHours(hours); - long ttlNanos = hours * 3600_000_000_000L; + LocalDateTime expiry = LocalDateTime.now().plusHours(hoursTtl); + long ttlNanos = hoursTtl * 3600_000_000_000L; return dht.publishValue(priv, pub, value, sequence, expiry, ttlNanos, node); } diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index bb929b29..ac398b81 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -47,7 +47,7 @@ public void publishValue() throws Exception { PrivKey publisher = Ed25519Kt.generateEd25519KeyPair().getFirst(); byte[] value = "This is a test".getBytes(); - node1.publishValue(publisher, value, 1).join(); + node1.publishValue(publisher, value, 1, 24).join(); byte[] res = node1.resolveValue(publisher.publicKey()).join(); Assert.assertTrue(Arrays.equals(res, value)); From c1f2a4634d34dada397a374f736499787da57c57 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 15:41:21 +0000 Subject: [PATCH 140/218] Fix concurrent modification exception in IPNS publish --- src/main/java/org/peergos/protocol/dht/Kademlia.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 85b850a3..3e821d65 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -388,7 +388,7 @@ public CompletableFuture publishValue(PrivKey priv, if (toQuery.size() == remaining) { // publish to closest remaining nodes while (publishes.size() < minPublishes) { - List closest = toQuery.stream() + List closest = new TreeSet<>(toQuery).stream() .limit(minPublishes - publishes.size() + 5) .collect(Collectors.toList()); List> lastFutures = closest.stream() From 457c7a60365dbc6bbb52d0cf67db0512db276a34 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 16:20:50 +0000 Subject: [PATCH 141/218] Generalise IPNS api to allow publishing pre-signed values --- src/main/java/org/peergos/EmbeddedIpfs.java | 7 ++++- .../org/peergos/protocol/dht/Kademlia.java | 26 ++++++----------- .../protocol/dht/KademliaController.java | 25 ---------------- .../java/org/peergos/protocol/ipns/IPNS.java | 29 +++++++++++++++++++ .../java/org/peergos/EmbeddedIpfsTest.java | 25 ++++++++++++++++ src/test/java/org/peergos/IpnsTest.java | 4 +-- 6 files changed, 71 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 234f5661..c9af94e5 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -105,7 +105,12 @@ public CompletableFuture publishValue(PrivKey priv, byte[] value, long seq Multihash pub = Multihash.deserialize(PeerId.fromPubKey(priv.publicKey()).getBytes()); LocalDateTime expiry = LocalDateTime.now().plusHours(hoursTtl); long ttlNanos = hoursTtl * 3600_000_000_000L; - return dht.publishValue(priv, pub, value, sequence, expiry, ttlNanos, node); + byte[] signedRecord = IPNS.createSignedRecord(value, expiry, sequence, ttlNanos, priv); + return dht.publishValue(pub, signedRecord, node); + } + + public CompletableFuture publishPresignedRecord(Multihash pub, byte[] presignedRecord) { + return dht.publishValue(pub, presignedRecord, node); } public CompletableFuture resolveValue(PubKey pub) { diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 3e821d65..93c7d591 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -313,31 +313,23 @@ public CompletableFuture publishIpnsValue(PrivKey priv, LocalDateTime expiry = LocalDateTime.now().plusHours(hours); long ttlNanos = hours * 3600_000_000_000L; byte[] publishValue = ("/ipfs/" + value).getBytes(); - return publishValue(priv, publisher, publishValue, sequence, expiry, ttlNanos, us); + byte[] signedRecord = IPNS.createSignedRecord(publishValue, expiry, sequence, ttlNanos, priv); + return publishValue(publisher, signedRecord, us); } - private boolean putValue(PrivKey priv, - Multihash publisher, - byte[] publishValue, - long sequence, - LocalDateTime expiry, - long ttlNanos, + private boolean putValue(Multihash publisher, + byte[] signedRecord, PeerAddresses peer, Host us) { try { return dialPeer(peer, us).join() - .putValue(publishValue, expiry, sequence, - ttlNanos, publisher, priv).join(); + .putValue(publisher, signedRecord).join(); } catch (Exception e) {} return false; } - public CompletableFuture publishValue(PrivKey priv, - Multihash publisher, - byte[] publishValue, - long sequence, - LocalDateTime expiry, - long ttlNanos, + public CompletableFuture publishValue(Multihash publisher, + byte[] signedRecord, Host us) { Set publishes = Collections.synchronizedSet(new HashSet<>()); int minPublishes = 20; @@ -370,7 +362,7 @@ public CompletableFuture publishValue(PrivKey priv, } } ioExec.submit(() -> { - if (putValue(priv, publisher, publishValue, sequence, expiry, ttlNanos, r.addresses, us)) + if (putValue(publisher, signedRecord, r.addresses, us)) publishes.add(r.addresses.peerId); }); return true; @@ -396,7 +388,7 @@ public CompletableFuture publishValue(PrivKey priv, toQuery.remove(r); queried.add(r.addresses.peerId); return ioExec.submit(() -> { - if (putValue(priv, publisher, publishValue, sequence, expiry, ttlNanos, r.addresses, us)) + if (putValue(publisher, signedRecord, r.addresses, us)) publishes.add(r.addresses.peerId); }); }) diff --git a/src/main/java/org/peergos/protocol/dht/KademliaController.java b/src/main/java/org/peergos/protocol/dht/KademliaController.java index e5f6aa99..98e4fbb0 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaController.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaController.java @@ -47,31 +47,6 @@ default CompletableFuture getProviders(Multihash block) { .thenApply(Providers::fromProtobuf); } - default CompletableFuture putValue(byte[] value, LocalDateTime expiry, long sequence, - long ttlNanos, Multihash peerId, PrivKey ourKey) { - byte[] cborEntryData = IPNS.createCborDataForIpnsEntry(value, expiry, - Ipns.IpnsEntry.ValidityType.EOL_VALUE, sequence, ttlNanos); - String expiryString = IPNS.formatExpiry(expiry); - byte[] signature = ourKey.sign(IPNS.createSigV2Data(cborEntryData)); - PubKey pubKey = ourKey.publicKey(); - byte[] pubKeyProtobuf = Crypto.PublicKey.newBuilder() - .setType(pubKey.getKeyType()) - .setData(ByteString.copyFrom(pubKey.raw())) - .build() - .toByteArray(); - byte[] ipnsEntry = Ipns.IpnsEntry.newBuilder() - .setSequence(sequence) - .setTtl(ttlNanos) - .setValue(ByteString.copyFrom(value)) - .setValidityType(Ipns.IpnsEntry.ValidityType.EOL) - .setValidity(ByteString.copyFrom(expiryString.getBytes())) - .setData(ByteString.copyFrom(cborEntryData)) - .setSignatureV2(ByteString.copyFrom(signature)) - .setPubKey(ByteString.copyFrom(pubKeyProtobuf)) // not needed with Ed25519 - .build().toByteArray(); - return putValue(peerId, ipnsEntry); - } - default CompletableFuture putValue(Multihash peerId, byte[] value) { byte[] ipnsRecordKey = IPNS.getKey(peerId); Dht.Message outgoing = Dht.Message.newBuilder() diff --git a/src/main/java/org/peergos/protocol/ipns/IPNS.java b/src/main/java/org/peergos/protocol/ipns/IPNS.java index 044a6a56..8210c1eb 100644 --- a/src/main/java/org/peergos/protocol/ipns/IPNS.java +++ b/src/main/java/org/peergos/protocol/ipns/IPNS.java @@ -34,6 +34,35 @@ public static byte[] getKey(Multihash peerId) { return bout.toByteArray(); } + public static byte[] createSignedRecord(byte[] value, + LocalDateTime expiry, + long sequence, + long ttlNanos, + PrivKey ourKey) { + byte[] cborEntryData = IPNS.createCborDataForIpnsEntry(value, expiry, + Ipns.IpnsEntry.ValidityType.EOL_VALUE, sequence, ttlNanos); + String expiryString = IPNS.formatExpiry(expiry); + byte[] signature = ourKey.sign(IPNS.createSigV2Data(cborEntryData)); + PubKey pubKey = ourKey.publicKey(); + Ipns.IpnsEntry.Builder entryBuilder = Ipns.IpnsEntry.newBuilder() + .setSequence(sequence) + .setTtl(ttlNanos) + .setValue(ByteString.copyFrom(value)) + .setValidityType(Ipns.IpnsEntry.ValidityType.EOL) + .setValidity(ByteString.copyFrom(expiryString.getBytes())) + .setData(ByteString.copyFrom(cborEntryData)) + .setSignatureV2(ByteString.copyFrom(signature)); + if (ourKey.getKeyType() != Crypto.KeyType.Ed25519) { + byte[] pubKeyProtobuf = Crypto.PublicKey.newBuilder() + .setType(pubKey.getKeyType()) + .setData(ByteString.copyFrom(pubKey.raw())) + .build() + .toByteArray(); + entryBuilder = entryBuilder.setPubKey(ByteString.copyFrom(pubKeyProtobuf)); // not needed with Ed25519 + } + return entryBuilder.build().toByteArray(); + } + public static Cid getCidFromKey(ByteString key) { if (! key.startsWith(ByteString.copyFrom("/ipns/".getBytes(StandardCharsets.UTF_8)))) throw new IllegalStateException("Unknown IPNS key space: " + key); diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index ac398b81..d8cb836b 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -3,6 +3,7 @@ import identify.pb.*; import io.ipfs.cid.*; import io.ipfs.multiaddr.*; +import io.ipfs.multihash.Multihash; import io.libp2p.core.*; import io.libp2p.core.crypto.*; import io.libp2p.core.multiformats.*; @@ -12,7 +13,9 @@ import org.peergos.blockstore.*; import org.peergos.config.*; import org.peergos.protocol.dht.*; +import org.peergos.protocol.ipns.*; +import java.time.*; import java.util.*; import java.util.concurrent.*; import java.util.stream.*; @@ -54,6 +57,28 @@ public void publishValue() throws Exception { node1.stop(); } + @Test + public void publishPresignedValue() throws Exception { + EmbeddedIpfs node1 = build(BootstrapTest.BOOTSTRAP_NODES, List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); + node1.start(); + + PrivKey publisher = Ed25519Kt.generateEd25519KeyPair().getFirst(); + byte[] value = "This is a test".getBytes(); + io.ipfs.multihash.Multihash pub = Multihash.deserialize(PeerId.fromPubKey(publisher.publicKey()).getBytes()); + long hoursTtl = 24*2; + LocalDateTime expiry = LocalDateTime.now().plusHours(hoursTtl); + long ttlNanos = hoursTtl * 3600_000_000_000L; + byte[] signedRecord = IPNS.createSignedRecord(value, expiry, 1, ttlNanos, publisher); + node1.publishPresignedRecord(pub, signedRecord).join(); + node1.publishPresignedRecord(pub, signedRecord).join(); + node1.publishPresignedRecord(pub, signedRecord).join(); + + byte[] res = node1.resolveValue(publisher.publicKey()).join(); + Assert.assertTrue(Arrays.equals(res, value)); + + node1.stop(); + } + @Test public void wildcardListenerAddressesGetExpanded() { int node1Port = TestPorts.getPort(); diff --git a/src/test/java/org/peergos/IpnsTest.java b/src/test/java/org/peergos/IpnsTest.java index 8ea57bff..72d4d3fa 100644 --- a/src/test/java/org/peergos/IpnsTest.java +++ b/src/test/java/org/peergos/IpnsTest.java @@ -2,7 +2,6 @@ import io.ipfs.api.*; import io.ipfs.cid.*; -import io.ipfs.multiaddr.*; import io.ipfs.multihash.Multihash; import io.libp2p.core.*; import io.libp2p.core.multiformats.*; @@ -48,8 +47,9 @@ public void publishIPNSRecordToKubo() throws IOException { for (int i = 0; i < 10; i++) { try { + byte[] value = IPNS.createSignedRecord(pathToPublish.getBytes(), expiry, sequence, ttl, node1.getPrivKey()); success = dht.dial(node1, address2).getController().join() - .putValue(pathToPublish.getBytes(), expiry, sequence, ttl, node1Id, node1.getPrivKey()) + .putValue(node1Id, value) .orTimeout(2, TimeUnit.SECONDS).join(); break; } catch (Exception timeout) { From f86c885879446f045d15b74ec4e1ad05bbb4e5d6 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 16:31:17 +0000 Subject: [PATCH 142/218] Test updating an IPNS value with the same expiry --- src/test/java/org/peergos/EmbeddedIpfsTest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index d8cb836b..ac6a3400 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -65,7 +65,7 @@ public void publishPresignedValue() throws Exception { PrivKey publisher = Ed25519Kt.generateEd25519KeyPair().getFirst(); byte[] value = "This is a test".getBytes(); io.ipfs.multihash.Multihash pub = Multihash.deserialize(PeerId.fromPubKey(publisher.publicKey()).getBytes()); - long hoursTtl = 24*2; + long hoursTtl = 24*365; LocalDateTime expiry = LocalDateTime.now().plusHours(hoursTtl); long ttlNanos = hoursTtl * 3600_000_000_000L; byte[] signedRecord = IPNS.createSignedRecord(value, expiry, 1, ttlNanos, publisher); @@ -76,6 +76,16 @@ public void publishPresignedValue() throws Exception { byte[] res = node1.resolveValue(publisher.publicKey()).join(); Assert.assertTrue(Arrays.equals(res, value)); + // publish an updated value with same expiry + byte[] value2 = "Updated value".getBytes(); + byte[] signedRecord2 = IPNS.createSignedRecord(value2, expiry, 2, ttlNanos, publisher); + node1.publishPresignedRecord(pub, signedRecord2).join(); + node1.publishPresignedRecord(pub, signedRecord2).join(); + node1.publishPresignedRecord(pub, signedRecord2).join(); + + byte[] res2 = node1.resolveValue(publisher.publicKey()).join(); + Assert.assertTrue(Arrays.equals(res2, value2)); + node1.stop(); } From 143c845a4c0b8a3eef3c75ca2ad17299ccc10ebc Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Dec 2023 17:11:36 +0000 Subject: [PATCH 143/218] Fix IPNS publish race condition --- .../org/peergos/protocol/dht/Kademlia.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 93c7d591..730f6bf6 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -336,7 +336,7 @@ public CompletableFuture publishValue(Multihash publisher, byte[] key = IPNS.getKey(publisher); Id keyId = Id.create(Hash.sha256(key), 256); - SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); + SortedSet toQuery = new TreeSet<>((a, b) -> compareKeys(a, b, keyId)); List localClosest = engine.getKClosestPeers(key); int queryParallelism = 3; toQuery.addAll(localClosest.stream() @@ -349,29 +349,30 @@ public CompletableFuture publishValue(Multihash publisher, List thisRound = toQuery.stream() .limit(queryParallelism) .collect(Collectors.toList()); - List> futures = thisRound.stream() + List>> futures = thisRound.stream() .map(r -> { toQuery.remove(r); queried.add(r.addresses.peerId); return ioExec.submit(() -> getCloserPeers(key, r.addresses, us).thenApply(res -> { - for (PeerAddresses peer : res) { - if (! queried.contains(peer.peerId)) { - Id peerKey = Id.create(Hash.sha256(IPNS.getKey(peer.peerId)), 256); - RoutingEntry e = new RoutingEntry(peerKey, peer); - toQuery.add(e); - } - } - ioExec.submit(() -> { - if (putValue(publisher, signedRecord, r.addresses, us)) - publishes.add(r.addresses.peerId); - }); - return true; - })); + List more = new ArrayList<>(); + for (PeerAddresses peer : res) { + if (! queried.contains(peer.peerId)) { + Id peerKey = Id.create(Hash.sha256(IPNS.getKey(peer.peerId)), 256); + RoutingEntry e = new RoutingEntry(peerKey, peer); + more.add(e); + } + } + ioExec.submit(() -> { + if (putValue(publisher, signedRecord, r.addresses, us)) + publishes.add(r.addresses.peerId); + }); + return more; + }).join()); }) .collect(Collectors.toList()); futures.forEach(f -> { try { - f.get(); + toQuery.addAll(f.get()); } catch (Exception e) {} }); // exit early if we have enough results @@ -380,7 +381,7 @@ public CompletableFuture publishValue(Multihash publisher, if (toQuery.size() == remaining) { // publish to closest remaining nodes while (publishes.size() < minPublishes) { - List closest = new TreeSet<>(toQuery).stream() + List closest = toQuery.stream() .limit(minPublishes - publishes.size() + 5) .collect(Collectors.toList()); List> lastFutures = closest.stream() From e4cfae802fad11717408d50491ce2757d3a1be49 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 14 Dec 2023 15:36:56 +0000 Subject: [PATCH 144/218] Test that sequence number has higher precedence than expiry --- src/test/java/org/peergos/EmbeddedIpfsTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index ac6a3400..5bf9e5aa 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -86,6 +86,16 @@ public void publishPresignedValue() throws Exception { byte[] res2 = node1.resolveValue(publisher.publicKey()).join(); Assert.assertTrue(Arrays.equals(res2, value2)); + // publish an updated value with earlier expiry + byte[] value3 = "3rd value to put in IPNS".getBytes(); + byte[] signedRecord3 = IPNS.createSignedRecord(value3, expiry.minusDays(1), 3, ttlNanos, publisher); + node1.publishPresignedRecord(pub, signedRecord3).join(); + node1.publishPresignedRecord(pub, signedRecord3).join(); + node1.publishPresignedRecord(pub, signedRecord3).join(); + + byte[] res3 = node1.resolveValue(publisher.publicKey()).join(); + Assert.assertTrue(Arrays.equals(res3, value3)); + node1.stop(); } From 6d7c107f048503402f397c1794e59f3133b43c68 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 14 Dec 2023 16:42:44 +0000 Subject: [PATCH 145/218] Guard against overwriting newer IPNS records Put record in local store during publish --- .../protocol/dht/DatabaseRecordStore.java | 2 +- .../java/org/peergos/protocol/dht/Kademlia.java | 11 ++++++++++- .../org/peergos/protocol/dht/KademliaEngine.java | 16 ++++++++++++++-- .../org/peergos/protocol/dht/RamRecordStore.java | 3 +-- .../org/peergos/protocol/dht/RecordStore.java | 2 +- .../org/peergos/protocol/ipns/GetResult.java | 2 +- .../java/org/peergos/protocol/ipns/IPNS.java | 14 +++++++++----- 7 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java b/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java index 16447c4b..9c4f43d9 100644 --- a/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java +++ b/src/main/java/org/peergos/protocol/dht/DatabaseRecordStore.java @@ -61,7 +61,7 @@ private String hashToKey(Multihash hash) { } @Override - public Optional get(Cid peerId) { + public Optional get(Multihash peerId) { String selectSQL = "SELECT raw, sequence, ttlNanos, expiryUTC, val FROM " + RECORD_TABLE + " WHERE peerId=?"; try (PreparedStatement pstmt = connection.prepareStatement(selectSQL)) { pstmt.setString(1, hashToKey(peerId)); diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 730f6bf6..528ae9fe 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -331,10 +331,19 @@ private boolean putValue(Multihash publisher, public CompletableFuture publishValue(Multihash publisher, byte[] signedRecord, Host us) { + byte[] key = IPNS.getKey(publisher); + Optional parsed = IPNS.parseAndValidateIpnsEntry(key, signedRecord); + if (parsed.isEmpty() || !parsed.get().publisher.equals(publisher)) + throw new IllegalStateException("Tried to publish invalid INS record for " + publisher); + Optional existing = engine.getRecord(publisher); + // don't overwrite 'newer' record + if (existing.isEmpty() || parsed.get().value.compareTo(existing.get()) > 0) { + engine.addRecord(publisher, parsed.get().value); + } + Set publishes = Collections.synchronizedSet(new HashSet<>()); int minPublishes = 20; - byte[] key = IPNS.getKey(publisher); Id keyId = Id.create(Hash.sha256(key), 256); SortedSet toQuery = new TreeSet<>((a, b) -> compareKeys(a, b, keyId)); List localClosest = engine.getKClosestPeers(key); diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index eaba4464..4c16c448 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -3,7 +3,6 @@ import com.google.protobuf.*; import com.offbynull.kademlia.*; import io.ipfs.cid.*; -import io.ipfs.multiaddr.*; import io.ipfs.multihash.Multihash; import io.libp2p.core.*; import io.libp2p.core.Stream; @@ -97,12 +96,25 @@ public List getKClosestPeers(byte[] key) { .collect(Collectors.toList()); } + public void addRecord(Multihash publisher, IpnsRecord record) { + ipnsStore.put(publisher, record); + } + + public Optional getRecord(Multihash publisher) { + return ipnsStore.get(publisher); + } + public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { responderReceivedBytes.inc(msg.getSerializedSize()); switch (msg.getType()) { case PUT_VALUE: { - Optional mapping = IPNS.validateIpnsEntry(msg); + Optional mapping = IPNS.parseAndValidateIpnsEntry(msg); if (mapping.isPresent()) { + Optional existing = ipnsStore.get(mapping.get().publisher); + if (existing.isPresent() && mapping.get().value.compareTo(existing.get()) < 0) { + // don't add 'older' record + return; + } ipnsStore.put(mapping.get().publisher, mapping.get().value); stream.writeAndFlush(msg); responderSentBytes.inc(msg.getSerializedSize()); diff --git a/src/main/java/org/peergos/protocol/dht/RamRecordStore.java b/src/main/java/org/peergos/protocol/dht/RamRecordStore.java index 7ead53c2..10d78190 100644 --- a/src/main/java/org/peergos/protocol/dht/RamRecordStore.java +++ b/src/main/java/org/peergos/protocol/dht/RamRecordStore.java @@ -1,6 +1,5 @@ package org.peergos.protocol.dht; -import io.ipfs.cid.*; import io.ipfs.multihash.*; import org.peergos.protocol.ipns.*; @@ -19,7 +18,7 @@ public void put(Multihash peerId, IpnsRecord record) { } @Override - public Optional get(Cid peerId) { + public Optional get(Multihash peerId) { return Optional.ofNullable(records.get(peerId)); } diff --git a/src/main/java/org/peergos/protocol/dht/RecordStore.java b/src/main/java/org/peergos/protocol/dht/RecordStore.java index 1a1229b9..f400ab0f 100644 --- a/src/main/java/org/peergos/protocol/dht/RecordStore.java +++ b/src/main/java/org/peergos/protocol/dht/RecordStore.java @@ -10,7 +10,7 @@ public interface RecordStore extends AutoCloseable { void put(Multihash peerId, IpnsRecord record); - Optional get(Cid peerId); + Optional get(Multihash peerId); void remove(Multihash peerId); } diff --git a/src/main/java/org/peergos/protocol/ipns/GetResult.java b/src/main/java/org/peergos/protocol/ipns/GetResult.java index 087ec117..709c56c0 100644 --- a/src/main/java/org/peergos/protocol/ipns/GetResult.java +++ b/src/main/java/org/peergos/protocol/ipns/GetResult.java @@ -20,7 +20,7 @@ public static GetResult fromProtobuf(Dht.Message msg) { .map(PeerAddresses::fromProtobuf) .collect(Collectors.toList()); Optional record = msg.hasRecord() ? - IPNS.validateIpnsEntry(msg) : + IPNS.parseAndValidateIpnsEntry(msg) : Optional.empty(); return new GetResult(record, closerPeers); } diff --git a/src/main/java/org/peergos/protocol/ipns/IPNS.java b/src/main/java/org/peergos/protocol/ipns/IPNS.java index 8210c1eb..c65e6ee8 100644 --- a/src/main/java/org/peergos/protocol/ipns/IPNS.java +++ b/src/main/java/org/peergos/protocol/ipns/IPNS.java @@ -95,17 +95,22 @@ public static byte[] createSigV2Data(byte[] data) { } } - public static Optional validateIpnsEntry(Dht.Message msg) { + public static Optional parseAndValidateIpnsEntry(Dht.Message msg) { if (! msg.hasRecord() || msg.getRecord().getValue().size() > IPNS.MAX_RECORD_SIZE) return Optional.empty(); if (! msg.getKey().equals(msg.getRecord().getKey())) return Optional.empty(); - if (! msg.getRecord().getKey().startsWith(ByteString.copyFrom("/ipns/".getBytes(StandardCharsets.UTF_8)))) + byte[] entryBytes = msg.getRecord().getValue().toByteArray(); + return parseAndValidateIpnsEntry(msg.getRecord().getKey().toByteArray(), entryBytes); + } + + public static Optional parseAndValidateIpnsEntry(byte[] key, byte[] entryBytes) { + if (! Arrays.equals(Arrays.copyOfRange(key, 0, 6), "/ipns/".getBytes(StandardCharsets.UTF_8))) return Optional.empty(); - byte[] cidBytes = msg.getRecord().getKey().substring(6).toByteArray(); + byte[] cidBytes = Arrays.copyOfRange(key, 6, key.length); Multihash signer = Multihash.deserialize(cidBytes); try { - Ipns.IpnsEntry entry = Ipns.IpnsEntry.parseFrom(msg.getRecord().getValue()); + Ipns.IpnsEntry entry = Ipns.IpnsEntry.parseFrom(entryBytes); if (! entry.hasSignatureV2() || ! entry.hasData()) return Optional.empty(); PubKey pub; @@ -137,7 +142,6 @@ public static Optional validateIpnsEntry(Dht.Message msg) { LocalDateTime expiry = LocalDateTime.parse(new String(validity).substring(0, validity.length - 1), IPNS.rfc3339nano); if (expiry.isBefore(LocalDateTime.now())) return Optional.empty(); - byte[] entryBytes = msg.getRecord().getValue().toByteArray(); IpnsRecord record = new IpnsRecord(entryBytes, entry.getSequence(), entry.getTtl(), expiry, entry.getValue().toByteArray()); return Optional.of(new IpnsMapping(signer, record)); } catch (InvalidProtocolBufferException e) { From 119bc18c56189c57d32e84dd1b24029987d59231 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 14 Dec 2023 16:56:03 +0000 Subject: [PATCH 146/218] Make minResults a parameter of resolveValue Include any local value in results --- src/main/java/org/peergos/EmbeddedIpfs.java | 4 ++-- src/main/java/org/peergos/protocol/dht/Kademlia.java | 3 +++ src/test/java/org/peergos/EmbeddedIpfsTest.java | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index c9af94e5..276b8232 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -113,9 +113,9 @@ public CompletableFuture publishPresignedRecord(Multihash pub, byte[] pres return dht.publishValue(pub, presignedRecord, node); } - public CompletableFuture resolveValue(PubKey pub) { + public CompletableFuture resolveValue(PubKey pub, int minResults) { Multihash publisher = Multihash.deserialize(PeerId.fromPubKey(pub).getBytes()); - List candidates = dht.resolveValue(publisher, 1, node); + List candidates = dht.resolveValue(publisher, minResults, node); List records = candidates.stream().sorted().collect(Collectors.toList()); if (records.isEmpty()) return CompletableFuture.failedFuture(new IllegalStateException("Couldn't resolve IPNS value for " + pub)); diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 528ae9fe..6c48e221 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -432,6 +432,9 @@ private Optional getValueFromPeer(PeerAddresses peer, Multihash publi public List resolveValue(Multihash publisher, int minResults, Host us) { byte[] key = IPNS.getKey(publisher); List candidates = new ArrayList<>(); + Optional local = engine.getRecord(publisher); + local.ifPresent(candidates::add); + Id keyId = Id.create(Hash.sha256(key), 256); SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); List localClosest = engine.getKClosestPeers(key); diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 5bf9e5aa..dab8a67d 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -51,7 +51,7 @@ public void publishValue() throws Exception { PrivKey publisher = Ed25519Kt.generateEd25519KeyPair().getFirst(); byte[] value = "This is a test".getBytes(); node1.publishValue(publisher, value, 1, 24).join(); - byte[] res = node1.resolveValue(publisher.publicKey()).join(); + byte[] res = node1.resolveValue(publisher.publicKey(), 5).join(); Assert.assertTrue(Arrays.equals(res, value)); node1.stop(); @@ -73,7 +73,7 @@ public void publishPresignedValue() throws Exception { node1.publishPresignedRecord(pub, signedRecord).join(); node1.publishPresignedRecord(pub, signedRecord).join(); - byte[] res = node1.resolveValue(publisher.publicKey()).join(); + byte[] res = node1.resolveValue(publisher.publicKey(), 5).join(); Assert.assertTrue(Arrays.equals(res, value)); // publish an updated value with same expiry @@ -83,7 +83,7 @@ public void publishPresignedValue() throws Exception { node1.publishPresignedRecord(pub, signedRecord2).join(); node1.publishPresignedRecord(pub, signedRecord2).join(); - byte[] res2 = node1.resolveValue(publisher.publicKey()).join(); + byte[] res2 = node1.resolveValue(publisher.publicKey(), 5).join(); Assert.assertTrue(Arrays.equals(res2, value2)); // publish an updated value with earlier expiry @@ -93,7 +93,7 @@ public void publishPresignedValue() throws Exception { node1.publishPresignedRecord(pub, signedRecord3).join(); node1.publishPresignedRecord(pub, signedRecord3).join(); - byte[] res3 = node1.resolveValue(publisher.publicKey()).join(); + byte[] res3 = node1.resolveValue(publisher.publicKey(), 5).join(); Assert.assertTrue(Arrays.equals(res3, value3)); node1.stop(); From 03eaf3ac0e5e1f44139efee6ad21d4a247691b70 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 18 Dec 2023 21:45:51 +0000 Subject: [PATCH 147/218] Return number of nodes IPNS is published to Make kclosest a parameter Don't bother dialling peers with no transport in common Increase minimum publishes to 40 from 20 Exit early within round in publish and resolve if criteria are met --- src/main/java/org/peergos/EmbeddedIpfs.java | 4 +- .../org/peergos/protocol/dht/Kademlia.java | 41 ++++++++++++------- .../peergos/protocol/dht/KademliaEngine.java | 10 ++--- src/test/java/org/peergos/KademliaTest.java | 11 ++--- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 276b8232..c3575a9e 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -101,7 +101,7 @@ public List getBlocks(List wants, Set peers, boolean .collect(Collectors.toList()); } - public CompletableFuture publishValue(PrivKey priv, byte[] value, long sequence, int hoursTtl) { + public CompletableFuture publishValue(PrivKey priv, byte[] value, long sequence, int hoursTtl) { Multihash pub = Multihash.deserialize(PeerId.fromPubKey(priv.publicKey()).getBytes()); LocalDateTime expiry = LocalDateTime.now().plusHours(hoursTtl); long ttlNanos = hoursTtl * 3600_000_000_000L; @@ -109,7 +109,7 @@ public CompletableFuture publishValue(PrivKey priv, byte[] value, long seq return dht.publishValue(pub, signedRecord, node); } - public CompletableFuture publishPresignedRecord(Multihash pub, byte[] presignedRecord) { + public CompletableFuture publishPresignedRecord(Multihash pub, byte[] presignedRecord) { return dht.publishValue(pub, presignedRecord, node); } diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 6c48e221..527929f9 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -6,6 +6,7 @@ import io.libp2p.core.*; import io.libp2p.core.crypto.*; import io.libp2p.core.multiformats.*; +import io.libp2p.core.multiformats.Protocol; import io.libp2p.core.multistream.*; import io.libp2p.etc.types.*; import io.libp2p.protocol.*; @@ -147,7 +148,7 @@ public List findClosestPeers(byte[] key, int maxCount, Host us) { Id keyId = Id.create(Hash.sha256(key), 256); SortedSet closest = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); - List localClosest = engine.getKClosestPeers(key); + List localClosest = engine.getKClosestPeers(key, maxCount); if (maxCount == 1) { Optional match = localClosest.stream().filter(p -> Arrays.equals(p.peerId.toBytes(), key)).findFirst(); if (match.isPresent()) @@ -161,6 +162,7 @@ public List findClosestPeers(byte[] key, int maxCount, Host us) { int queryParallelism = 3; while (true) { List thisRound = toQuery.stream() + .filter(r -> hasTransportOverlap(r.addresses)) // don't waste time trying to dial nodes we can't .limit(queryParallelism) .collect(Collectors.toList()); List>> futures = thisRound.stream() @@ -207,7 +209,7 @@ public CompletableFuture> findProviders(Multihash block, Hos providers.addAll(engine.getProviders(block)); SortedSet toQuery = new TreeSet<>((a, b) -> b.key.getSharedPrefixLength(keyId) - a.key.getSharedPrefixLength(keyId)); - toQuery.addAll(engine.getKClosestPeers(key).stream() + toQuery.addAll(engine.getKClosestPeers(key, 20).stream() .map(p -> new RoutingEntry(Id.create(Hash.sha256(p.peerId.toBytes()), 256), p)) .collect(Collectors.toList())); @@ -304,11 +306,11 @@ public CompletableFuture provideBlock(Multihash block, Host us, PeerAddres return CompletableFuture.allOf(provides.toArray(new CompletableFuture[0])); } - public CompletableFuture publishIpnsValue(PrivKey priv, - Multihash publisher, - Multihash value, - long sequence, - Host us) { + public CompletableFuture publishIpnsValue(PrivKey priv, + Multihash publisher, + Multihash value, + long sequence, + Host us) { int hours = 1; LocalDateTime expiry = LocalDateTime.now().plusHours(hours); long ttlNanos = hours * 3600_000_000_000L; @@ -328,9 +330,13 @@ private boolean putValue(Multihash publisher, return false; } - public CompletableFuture publishValue(Multihash publisher, - byte[] signedRecord, - Host us) { + private boolean hasTransportOverlap(PeerAddresses p) { + return p.addresses.stream().anyMatch(a -> a.has(Protocol.TCP) && ! a.has(Protocol.P2PCIRCUIT)); + } + + public CompletableFuture publishValue(Multihash publisher, + byte[] signedRecord, + Host us) { byte[] key = IPNS.getKey(publisher); Optional parsed = IPNS.parseAndValidateIpnsEntry(key, signedRecord); if (parsed.isEmpty() || !parsed.get().publisher.equals(publisher)) @@ -342,11 +348,11 @@ public CompletableFuture publishValue(Multihash publisher, } Set publishes = Collections.synchronizedSet(new HashSet<>()); - int minPublishes = 20; + int minPublishes = 40; Id keyId = Id.create(Hash.sha256(key), 256); SortedSet toQuery = new TreeSet<>((a, b) -> compareKeys(a, b, keyId)); - List localClosest = engine.getKClosestPeers(key); + List localClosest = engine.getKClosestPeers(key, minPublishes); int queryParallelism = 3; toQuery.addAll(localClosest.stream() .limit(queryParallelism) @@ -356,6 +362,7 @@ public CompletableFuture publishValue(Multihash publisher, while (! toQuery.isEmpty()) { int remaining = toQuery.size() - 3; List thisRound = toQuery.stream() + .filter(r -> hasTransportOverlap(r.addresses)) // don't waste time trying to dial nodes we can't .limit(queryParallelism) .collect(Collectors.toList()); List>> futures = thisRound.stream() @@ -381,6 +388,8 @@ public CompletableFuture publishValue(Multihash publisher, .collect(Collectors.toList()); futures.forEach(f -> { try { + if (publishes.size() >= minPublishes) + return; toQuery.addAll(f.get()); } catch (Exception e) {} }); @@ -389,6 +398,7 @@ public CompletableFuture publishValue(Multihash publisher, break; if (toQuery.size() == remaining) { // publish to closest remaining nodes + System.out.println("Publishing to further nodes, so far only " + publishes.size()); while (publishes.size() < minPublishes) { List closest = toQuery.stream() .limit(minPublishes - publishes.size() + 5) @@ -412,7 +422,7 @@ public CompletableFuture publishValue(Multihash publisher, break; } } - return CompletableFuture.completedFuture(null); + return CompletableFuture.completedFuture(publishes.size()); } public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, int minResults) { @@ -437,7 +447,7 @@ public List resolveValue(Multihash publisher, int minResults, Host u Id keyId = Id.create(Hash.sha256(key), 256); SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); - List localClosest = engine.getKClosestPeers(key); + List localClosest = engine.getKClosestPeers(key, 20); int queryParallelism = 3; toQuery.addAll(localClosest.stream() .limit(queryParallelism) @@ -447,6 +457,7 @@ public List resolveValue(Multihash publisher, int minResults, Host u while (! toQuery.isEmpty()) { int remaining = toQuery.size() - 3; List thisRound = toQuery.stream() + .filter(r -> hasTransportOverlap(r.addresses)) // don't waste time trying to dial nodes we can't .limit(queryParallelism) .collect(Collectors.toList()); List> futures = thisRound.stream() @@ -469,6 +480,8 @@ public List resolveValue(Multihash publisher, int minResults, Host u .collect(Collectors.toList()); futures.forEach(f -> { try { + if (candidates.size() >= minResults) + return; f.get(); } catch (Exception e) {} }); diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 4c16c448..3908322e 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -43,6 +43,7 @@ public class KademliaEngine { .help("Total sent bytes in kademlia findNode protocol responder") .register(); + private static final int BUCKET_SIZE = 20; private final ProviderStore providersStore; private final RecordStore ipnsStore; public final Router router; @@ -81,8 +82,7 @@ public Set getProviders(Multihash h) { .collect(Collectors.toSet()); } - public List getKClosestPeers(byte[] key) { - int k = 20; + public List getKClosestPeers(byte[] key, int k) { List nodes; synchronized (this) { nodes = router.find(Id.create(Hash.sha256(key), 256), k, false); @@ -130,7 +130,7 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { builder = builder.setRecord(Dht.Record.newBuilder() .setKey(msg.getKey()) .setValue(ByteString.copyFrom(ipnsRecord.get().raw)).build()); - builder = builder.addAllCloserPeers(getKClosestPeers(msg.getKey().toByteArray()) + builder = builder.addAllCloserPeers(getKClosestPeers(msg.getKey().toByteArray(), BUCKET_SIZE) .stream() .map(p -> p.toProtobuf(a -> isPublic(a))) .collect(Collectors.toList())); @@ -163,7 +163,7 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { Dht.Message.Builder builder = msg.toBuilder(); builder = builder.addAllProviderPeers(providers.stream() .collect(Collectors.toList())); - builder = builder.addAllCloserPeers(getKClosestPeers(msg.getKey().toByteArray()) + builder = builder.addAllCloserPeers(getKClosestPeers(msg.getKey().toByteArray(), BUCKET_SIZE) .stream() .map(p -> p.toProtobuf(a -> isPublic(a))) .collect(Collectors.toList())); @@ -177,7 +177,7 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { Dht.Message.Builder builder = msg.toBuilder(); Multihash sourcePeer = Multihash.deserialize(source.getBytes()); byte[] target = msg.getKey().toByteArray(); - builder = builder.addAllCloserPeers(getKClosestPeers(target) + builder = builder.addAllCloserPeers(getKClosestPeers(target, BUCKET_SIZE) .stream() .filter(p -> ! p.peerId.equals(sourcePeer)) // don't tell a peer about themselves .map(p -> p.toProtobuf(a -> isPublic(a))) diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index b9517cc2..8fd8d94e 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -72,7 +72,7 @@ public void ipnsBenchmark() throws Exception { node2.start().join(); IdentifyBuilder.addIdentifyProtocol(node2); - Cid value = blockstore1.put("Provide me.".getBytes(), Cid.Codec.Raw).join(); + Cid value = blockstore1.put("Publiah me.".getBytes(), Cid.Codec.Raw).join(); try { // bootstrap node 1 @@ -94,9 +94,9 @@ public void ipnsBenchmark() throws Exception { signers.add(signer); Multihash pub = Multihash.deserialize(PeerId.fromPubKey(signer.publicKey()).getBytes()); long p0 = System.currentTimeMillis(); - dht1.publishIpnsValue(signer, pub, value, 1, node1).join(); + int publishes = dht1.publishIpnsValue(signer, pub, value, 1, node1).join(); long p1 = System.currentTimeMillis(); - System.out.println("Publish took " + printSeconds(p1-p0) + "s"); + System.out.println("Publish took " + printSeconds(p1-p0) + "s to " + publishes + " peers."); publishTotal += p1-p0; // retrieve it from node 2 @@ -107,7 +107,8 @@ public void ipnsBenchmark() throws Exception { System.out.println("Resolved in " + printSeconds(t1 - t0) + "s"); resolveTotal += t1-t0; } - System.out.println("Publish av: " + publishTotal/iterations + ", resolve av: " + resolveTotal/iterations); + System.out.println("Publish av: " + printSeconds(publishTotal/iterations) + + ", resolve av: " + printSeconds(resolveTotal/iterations)); // retrieve all again for (PrivKey signer : signers) { @@ -142,7 +143,7 @@ public void kademliaFindNodeLimitTest() { addrs.addAddrs(peer, 0, new Multiaddr[]{new Multiaddr("/ip4/127.0.0.1/tcp/4001/p2p/" + peer.toBase58())}); } } - List closest = kad.getKClosestPeers(new byte[32]); + List closest = kad.getKClosestPeers(new byte[32], 20); Assert.assertTrue(closest.size() <= 20); for (PeerAddresses addr : closest) { Assert.assertTrue(addr.addresses.size() == 1); From 3b06e93df48667026cb25a903d1902bdd586857d Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 19 Dec 2023 20:18:10 +0000 Subject: [PATCH 148/218] Change min ipns publishes to 30 --- src/main/java/org/peergos/protocol/dht/Kademlia.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 527929f9..c1e47318 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -348,7 +348,7 @@ public CompletableFuture publishValue(Multihash publisher, } Set publishes = Collections.synchronizedSet(new HashSet<>()); - int minPublishes = 40; + int minPublishes = 30; Id keyId = Id.create(Hash.sha256(key), 256); SortedSet toQuery = new TreeSet<>((a, b) -> compareKeys(a, b, keyId)); From 04e2723326e102c2b79c5a30c42772afc66b8993 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 20 Dec 2023 21:21:00 +0000 Subject: [PATCH 149/218] Write utility for bulk publishing and resolving of ipns Improve ipns tail resolvability Add EmbeddedIpfs.resolveRecords for getting all results found --- src/main/java/org/peergos/EmbeddedIpfs.java | 6 + src/main/java/org/peergos/IpnsPublisher.java | 105 ++++++++++++++++++ .../org/peergos/protocol/dht/Kademlia.java | 11 +- 3 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/peergos/IpnsPublisher.java diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index c3575a9e..2dae613f 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -122,6 +122,12 @@ public CompletableFuture resolveValue(PubKey pub, int minResults) { return CompletableFuture.completedFuture(records.get(records.size() - 1).value); } + public List resolveRecords(PubKey pub, int minResults) { + Multihash publisher = Multihash.deserialize(PeerId.fromPubKey(pub).getBytes()); + List candidates = dht.resolveValue(publisher, minResults, node); + return candidates.stream().sorted().collect(Collectors.toList()); + } + public void start() { LOG.info("Starting IPFS..."); Thread shutdownHook = new Thread(() -> { diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java new file mode 100644 index 00000000..f722197e --- /dev/null +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -0,0 +1,105 @@ +package org.peergos; + +import io.ipfs.multiaddr.*; +import io.ipfs.multihash.*; +import io.libp2p.core.*; +import io.libp2p.core.crypto.*; +import io.libp2p.crypto.keys.*; +import org.peergos.blockstore.*; +import org.peergos.config.*; +import org.peergos.protocol.dht.*; +import org.peergos.protocol.ipns.*; +import org.peergos.util.*; + +import java.io.*; +import java.nio.file.*; +import java.time.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.stream.*; + +public class IpnsPublisher { + private static final ExecutorService ioExec = Executors.newFixedThreadPool(20); + public static void main(String[] a) throws Exception { + Path keysFile = Paths.get("publishers.txt"); + List keys; + int keycount = 1000; + EmbeddedIpfs ipfs = startIpfs(); + if (keysFile.toFile().exists()) { + List lines = Files.readAllLines(keysFile); + keys = lines.stream() + .map(line -> KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line))) + .collect(Collectors.toList()); + + for (int c=0; c < 100; c++) { + long t0 = System.currentTimeMillis(); + List recordCounts = resolve(keys, ipfs); + Files.write(Paths.get("publish-resolve-counts-" + LocalDateTime.now().withNano(0) + ".txt"), recordCounts.stream() + .map(i -> i.toString()) + .collect(Collectors.toList())); + long t1 = System.currentTimeMillis(); + System.out.println("Resolved " + recordCounts.stream().filter(n -> n > 0).count() + "/" + recordCounts.size() + + " in " + (t1-t0)/1000 + "s"); + } + } else { + keys = IntStream.range(0, keycount) + .mapToObj(i -> Ed25519Kt.generateEd25519KeyPair().getFirst()) + .collect(Collectors.toList()); + Files.write(keysFile, keys.stream().map(k -> ArrayOps.bytesToHex(k.bytes())).collect(Collectors.toList())); + long t0 = System.currentTimeMillis(); + List publishCounts = publish(keys, "The result".getBytes(), ipfs); + long t1 = System.currentTimeMillis(); + System.out.println("Published all in " + (t1-t0)/1000 + "s"); + Files.write(Paths.get("publish-counts.txt"), publishCounts.stream() + .map(i -> i.toString()) + .collect(Collectors.toList())); + } + ipfs.stop().join(); + System.exit(0); + } + + public static List publish(List publishers, byte[] value, EmbeddedIpfs ipfs) throws IOException { + LocalDateTime expiry = LocalDateTime.now().plusDays(7); + AtomicLong done = new AtomicLong(0); + long ttlNanos = 7L * 24 * 3600 * 1000_000_000; + List> values = publishers.stream() + .map(p -> new Pair<>(Multihash.deserialize(PeerId.fromPubKey(p.publicKey()).getBytes()), + IPNS.createSignedRecord(value, expiry, 1, ttlNanos, p))) + .collect(Collectors.toList()); + Files.write(Paths.get("publish-values.txt"), values.stream() + .map(v -> ArrayOps.bytesToHex(v.right)) + .collect(Collectors.toList())); + List> futs = values.stream() + .map(v -> CompletableFuture.supplyAsync(() -> { + Integer res = ipfs.publishPresignedRecord(v.left, v.right).join(); + System.out.println(done.incrementAndGet()); + return res; + }, ioExec)) + .collect(Collectors.toList()); + return futs.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + } + + public static List resolve(List publishers, EmbeddedIpfs ipfs) { + List res = new ArrayList<>(); + for (PrivKey publisher : publishers) { + List records = ipfs.resolveRecords(publisher.publicKey(), 30); + res.add(records.size()); + } + return res; + } + + public static EmbeddedIpfs startIpfs() { + HostBuilder builder = new HostBuilder().generateIdentity(); + PrivKey privKey = builder.getPrivateKey(); + PeerId peerId = builder.getPeerId(); + IdentitySection id = new IdentitySection(privKey.bytes(), peerId); + EmbeddedIpfs ipfs = EmbeddedIpfs.build(new RamRecordStore(), new RamBlockstore(), false, + List.of(new MultiAddress("/ip6/::/tcp/0")), Config.defaultBootstrapNodes, id, + (c, s, au) -> CompletableFuture.completedFuture(true), Optional.empty()); + ipfs.start(); + return ipfs; + } +} diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index c1e47318..f7127914 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -435,7 +435,8 @@ public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, private Optional getValueFromPeer(PeerAddresses peer, Multihash publisher, Host us) { try { - return Optional.of(dialPeer(peer, us).join().getValue(publisher).join()); + return Optional.of(dialPeer(peer, us).orTimeout(1, TimeUnit.SECONDS).join() + .getValue(publisher).orTimeout(1, TimeUnit.SECONDS).join()); } catch (Exception e) {} return Optional.empty(); } @@ -450,14 +451,14 @@ public List resolveValue(Multihash publisher, int minResults, Host u List localClosest = engine.getKClosestPeers(key, 20); int queryParallelism = 3; toQuery.addAll(localClosest.stream() - .limit(queryParallelism) + .filter(p -> hasTransportOverlap(p)) // don't waste time trying to dial nodes we can't .map(p -> new RoutingEntry(Id.create(Hash.sha256(p.peerId.toBytes()), 256), p)) .collect(Collectors.toList())); Set queried = Collections.synchronizedSet(new HashSet<>()); + int countdown = 20; while (! toQuery.isEmpty()) { int remaining = toQuery.size() - 3; List thisRound = toQuery.stream() - .filter(r -> hasTransportOverlap(r.addresses)) // don't waste time trying to dial nodes we can't .limit(queryParallelism) .collect(Collectors.toList()); List> futures = thisRound.stream() @@ -469,7 +470,7 @@ public List resolveValue(Multihash publisher, int minResults, Host u if (g.record.isPresent() && g.record.get().publisher.equals(publisher)) candidates.add(g.record.get().value); for (PeerAddresses peer : g.closerPeers) { - if (! queried.contains(peer.peerId)) { + if (! queried.contains(peer.peerId) && hasTransportOverlap(peer)) { Id peerKey = Id.create(Hash.sha256(IPNS.getKey(peer.peerId)), 256); RoutingEntry e = new RoutingEntry(peerKey, peer); toQuery.add(e); @@ -489,6 +490,8 @@ public List resolveValue(Multihash publisher, int minResults, Host u if (candidates.size() >= minResults) break; if (toQuery.size() == remaining) + countdown--; + if (countdown <= 0) break; } return candidates; From 638589fe4bb8ebc3d43b24ce3db13f5b4db1c19b Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 21 Dec 2023 00:34:34 +0000 Subject: [PATCH 150/218] Make ipns publisher print results incrementally --- src/main/java/org/peergos/IpnsPublisher.java | 76 ++++++++++++-------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index f722197e..1ae9dee1 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -18,18 +18,19 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.util.stream.*; +import java.util.stream.Stream; public class IpnsPublisher { - private static final ExecutorService ioExec = Executors.newFixedThreadPool(20); + private static final ExecutorService ioExec = Executors.newFixedThreadPool(10); public static void main(String[] a) throws Exception { - Path keysFile = Paths.get("publishers.txt"); + Path publishFile = Paths.get("publishers.txt"); List keys; int keycount = 1000; EmbeddedIpfs ipfs = startIpfs(); - if (keysFile.toFile().exists()) { - List lines = Files.readAllLines(keysFile); + if (publishFile.toFile().exists()) { + List lines = Files.readAllLines(publishFile); keys = lines.stream() - .map(line -> KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line))) + .map(line -> KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line.split(" ")[0]))) .collect(Collectors.toList()); for (int c=0; c < 100; c++) { @@ -46,40 +47,59 @@ public static void main(String[] a) throws Exception { keys = IntStream.range(0, keycount) .mapToObj(i -> Ed25519Kt.generateEd25519KeyPair().getFirst()) .collect(Collectors.toList()); - Files.write(keysFile, keys.stream().map(k -> ArrayOps.bytesToHex(k.bytes())).collect(Collectors.toList())); long t0 = System.currentTimeMillis(); - List publishCounts = publish(keys, "The result".getBytes(), ipfs); + publish(keys, "The result".getBytes(), ipfs).forEach(res -> { + try { + Files.write(publishFile, res.toString().getBytes(), + publishFile.toFile().exists() ? + StandardOpenOption.APPEND : + StandardOpenOption.CREATE_NEW); + } catch (IOException e) { + e.printStackTrace(); + } + }); long t1 = System.currentTimeMillis(); System.out.println("Published all in " + (t1-t0)/1000 + "s"); - Files.write(Paths.get("publish-counts.txt"), publishCounts.stream() - .map(i -> i.toString()) - .collect(Collectors.toList())); } ipfs.stop().join(); System.exit(0); } - public static List publish(List publishers, byte[] value, EmbeddedIpfs ipfs) throws IOException { + public static class PublishResult { + public final PrivKey priv; + public final Multihash pub; + public final byte[] record; + public final int publishCount; + + public PublishResult(PrivKey priv, Multihash pub, byte[] record, int publishCount) { + this.priv = priv; + this.pub = pub; + this.record = record; + this.publishCount = publishCount; + } + + @Override + public String toString() { + return ArrayOps.bytesToHex(priv.bytes()) + " " + pub + " " + ArrayOps.bytesToHex(record) + + " " + publishCount + "\n"; + } + } + + public static Stream publish(List publishers, byte[] value, EmbeddedIpfs ipfs) throws IOException { LocalDateTime expiry = LocalDateTime.now().plusDays(7); AtomicLong done = new AtomicLong(0); long ttlNanos = 7L * 24 * 3600 * 1000_000_000; - List> values = publishers.stream() - .map(p -> new Pair<>(Multihash.deserialize(PeerId.fromPubKey(p.publicKey()).getBytes()), - IPNS.createSignedRecord(value, expiry, 1, ttlNanos, p))) - .collect(Collectors.toList()); - Files.write(Paths.get("publish-values.txt"), values.stream() - .map(v -> ArrayOps.bytesToHex(v.right)) - .collect(Collectors.toList())); - List> futs = values.stream() - .map(v -> CompletableFuture.supplyAsync(() -> { - Integer res = ipfs.publishPresignedRecord(v.left, v.right).join(); - System.out.println(done.incrementAndGet()); - return res; - }, ioExec)) - .collect(Collectors.toList()); - return futs.stream() - .map(CompletableFuture::join) - .collect(Collectors.toList()); + return publishers.stream() + .map(p -> { + Multihash pub = Multihash.deserialize(PeerId.fromPubKey(p.publicKey()).getBytes()); + byte[] record = IPNS.createSignedRecord(value, expiry, 1, ttlNanos, p); + return CompletableFuture.supplyAsync(() -> { + int count = ipfs.publishPresignedRecord(pub, record).join(); + System.out.println(done.incrementAndGet()); + return new PublishResult(p, pub, record, count); + }, ioExec); + }) + .map(CompletableFuture::join); } public static List resolve(List publishers, EmbeddedIpfs ipfs) { From d4c7aea169eb7895582005f9eb81b937b6a53190 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 21 Dec 2023 08:31:28 +0000 Subject: [PATCH 151/218] Improve ipns publisher logging --- src/main/java/org/peergos/IpnsPublisher.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index 1ae9dee1..a87868de 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -33,15 +33,20 @@ public static void main(String[] a) throws Exception { .map(line -> KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line.split(" ")[0]))) .collect(Collectors.toList()); + System.out.println("Resolving " + keys.size() + " keys"); for (int c=0; c < 100; c++) { long t0 = System.currentTimeMillis(); List recordCounts = resolve(keys, ipfs); - Files.write(Paths.get("publish-resolve-counts-" + LocalDateTime.now().withNano(0) + ".txt"), recordCounts.stream() - .map(i -> i.toString()) - .collect(Collectors.toList())); + Path resultsFile = Paths.get("publish-resolve-counts-" + LocalDateTime.now().withNano(0) + ".txt"); + Files.write(resultsFile, + recordCounts.stream() + .map(Object::toString) + .collect(Collectors.toList())); long t1 = System.currentTimeMillis(); - System.out.println("Resolved " + recordCounts.stream().filter(n -> n > 0).count() + "/" + recordCounts.size() - + " in " + (t1-t0)/1000 + "s"); + String total = "Resolved " + recordCounts.stream().filter(n -> n > 0).count() + "/" + recordCounts.size() + + " in " + (t1 - t0) / 1000 + "s"; + System.out.println(total); + Files.write(resultsFile, total.getBytes(), StandardOpenOption.APPEND); } } else { keys = IntStream.range(0, keycount) @@ -104,9 +109,13 @@ public static Stream publish(List publishers, byte[] val public static List resolve(List publishers, EmbeddedIpfs ipfs) { List res = new ArrayList<>(); + int done = 0; for (PrivKey publisher : publishers) { List records = ipfs.resolveRecords(publisher.publicKey(), 30); res.add(records.size()); + done++; + if (done % 10 == 0) + System.out.println("resolved " + done); } return res; } From d73911b7a11d9bb4efc50a0990fc078fd7899a3f Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 22 Dec 2023 09:28:58 +0000 Subject: [PATCH 152/218] Print which record failed to resolve in ipns publisher --- src/main/java/org/peergos/IpnsPublisher.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index a87868de..0b9479fd 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -47,6 +47,12 @@ public static void main(String[] a) throws Exception { + " in " + (t1 - t0) / 1000 + "s"; System.out.println(total); Files.write(resultsFile, total.getBytes(), StandardOpenOption.APPEND); + String fails = "\nFailed " + IntStream.range(0, recordCounts.size()) + .filter(i -> recordCounts.get(i) == 0) + .mapToObj(i -> i) + .collect(Collectors.toList()); + System.out.println(fails); + Files.write(resultsFile, fails.getBytes(), StandardOpenOption.APPEND); } } else { keys = IntStream.range(0, keycount) From aa5311825cb1c965bc510e7544c7d66508e8509a Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 23 Dec 2023 05:48:52 +0000 Subject: [PATCH 153/218] Republish after each resolve in IpnsPublisher --- src/main/java/org/peergos/IpnsPublisher.java | 41 +++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index 0b9479fd..d82ad5d3 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -16,7 +16,6 @@ import java.time.*; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.*; import java.util.stream.*; import java.util.stream.Stream; @@ -32,6 +31,12 @@ public static void main(String[] a) throws Exception { keys = lines.stream() .map(line -> KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line.split(" ")[0]))) .collect(Collectors.toList()); + List records = lines.stream() + .map(line -> new PublishResult(KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line.split(" ")[0])), + Multihash.fromBase58(line.split(" ")[1]), + ArrayOps.hexToBytes(line.split(" ")[2]), + 0)) + .collect(Collectors.toList()); System.out.println("Resolving " + keys.size() + " keys"); for (int c=0; c < 100; c++) { @@ -53,15 +58,18 @@ public static void main(String[] a) throws Exception { .collect(Collectors.toList()); System.out.println(fails); Files.write(resultsFile, fails.getBytes(), StandardOpenOption.APPEND); + publish(records, ipfs); } } else { keys = IntStream.range(0, keycount) .mapToObj(i -> Ed25519Kt.generateEd25519KeyPair().getFirst()) .collect(Collectors.toList()); long t0 = System.currentTimeMillis(); - publish(keys, "The result".getBytes(), ipfs).forEach(res -> { + List> futs = publish(keys, "The result".getBytes(), ipfs) + .collect(Collectors.toList()); + futs.forEach(res -> { try { - Files.write(publishFile, res.toString().getBytes(), + Files.write(publishFile, res.join().toString().getBytes(), publishFile.toFile().exists() ? StandardOpenOption.APPEND : StandardOpenOption.CREATE_NEW); @@ -96,21 +104,28 @@ public String toString() { } } - public static Stream publish(List publishers, byte[] value, EmbeddedIpfs ipfs) throws IOException { + public static Stream> publish(List publishers, byte[] value, EmbeddedIpfs ipfs) throws IOException { LocalDateTime expiry = LocalDateTime.now().plusDays(7); - AtomicLong done = new AtomicLong(0); long ttlNanos = 7L * 24 * 3600 * 1000_000_000; - return publishers.stream() + List signed = publishers.stream() .map(p -> { Multihash pub = Multihash.deserialize(PeerId.fromPubKey(p.publicKey()).getBytes()); byte[] record = IPNS.createSignedRecord(value, expiry, 1, ttlNanos, p); - return CompletableFuture.supplyAsync(() -> { - int count = ipfs.publishPresignedRecord(pub, record).join(); - System.out.println(done.incrementAndGet()); - return new PublishResult(p, pub, record, count); - }, ioExec); - }) - .map(CompletableFuture::join); + return new PublishResult(p, pub, record, 0); + }).collect(Collectors.toList()); + return publish(signed, ipfs); + } + + public static Stream> publish(List pubs, EmbeddedIpfs ipfs) { + return pubs.stream() + .map(p -> publish(p, ipfs)); + } + + public static CompletableFuture publish(PublishResult signed, EmbeddedIpfs ipfs) { + return CompletableFuture.supplyAsync(() -> { + int count = ipfs.publishPresignedRecord(signed.pub, signed.record).join(); + return new PublishResult(signed.priv, signed.pub, signed.record, count); + }, ioExec); } public static List resolve(List publishers, EmbeddedIpfs ipfs) { From b6a16b731c7e486e4a0a4a9ff906763f20578dc9 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 27 Dec 2023 06:46:35 +0000 Subject: [PATCH 154/218] Republish after each resolve in IpnsPublisher --- src/main/java/org/peergos/IpnsPublisher.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index d82ad5d3..db6a216c 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -28,9 +28,6 @@ public static void main(String[] a) throws Exception { EmbeddedIpfs ipfs = startIpfs(); if (publishFile.toFile().exists()) { List lines = Files.readAllLines(publishFile); - keys = lines.stream() - .map(line -> KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line.split(" ")[0]))) - .collect(Collectors.toList()); List records = lines.stream() .map(line -> new PublishResult(KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line.split(" ")[0])), Multihash.fromBase58(line.split(" ")[1]), @@ -38,10 +35,10 @@ public static void main(String[] a) throws Exception { 0)) .collect(Collectors.toList()); - System.out.println("Resolving " + keys.size() + " keys"); + System.out.println("Resolving " + records.size() + " keys"); for (int c=0; c < 100; c++) { long t0 = System.currentTimeMillis(); - List recordCounts = resolve(keys, ipfs); + List recordCounts = resolveAndRepublish(records, ipfs); Path resultsFile = Paths.get("publish-resolve-counts-" + LocalDateTime.now().withNano(0) + ".txt"); Files.write(resultsFile, recordCounts.stream() @@ -58,7 +55,6 @@ public static void main(String[] a) throws Exception { .collect(Collectors.toList()); System.out.println(fails); Files.write(resultsFile, fails.getBytes(), StandardOpenOption.APPEND); - publish(records, ipfs); } } else { keys = IntStream.range(0, keycount) @@ -128,15 +124,16 @@ public static CompletableFuture publish(PublishResult signed, Emb }, ioExec); } - public static List resolve(List publishers, EmbeddedIpfs ipfs) { + public static List resolveAndRepublish(List publishers, EmbeddedIpfs ipfs) { List res = new ArrayList<>(); int done = 0; - for (PrivKey publisher : publishers) { - List records = ipfs.resolveRecords(publisher.publicKey(), 30); + for (PublishResult pub : publishers) { + List records = ipfs.resolveRecords(pub.priv.publicKey(), 30); res.add(records.size()); done++; if (done % 10 == 0) System.out.println("resolved " + done); + CompletableFuture.supplyAsync(() -> ipfs.publishPresignedRecord(pub.pub, pub.record)); } return res; } From e6f3b45de75ded50c47dbd95c23b161586e33f4d Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 27 Dec 2023 09:23:40 +0000 Subject: [PATCH 155/218] Use a different peer to publish and reoslve in IpnsPublisher --- src/main/java/org/peergos/IpnsPublisher.java | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index db6a216c..8529f451 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -23,9 +23,9 @@ public class IpnsPublisher { private static final ExecutorService ioExec = Executors.newFixedThreadPool(10); public static void main(String[] a) throws Exception { Path publishFile = Paths.get("publishers.txt"); - List keys; int keycount = 1000; - EmbeddedIpfs ipfs = startIpfs(); + EmbeddedIpfs publisher = startIpfs(); + EmbeddedIpfs resolver = startIpfs(); if (publishFile.toFile().exists()) { List lines = Files.readAllLines(publishFile); List records = lines.stream() @@ -38,7 +38,7 @@ public static void main(String[] a) throws Exception { System.out.println("Resolving " + records.size() + " keys"); for (int c=0; c < 100; c++) { long t0 = System.currentTimeMillis(); - List recordCounts = resolveAndRepublish(records, ipfs); + List recordCounts = resolveAndRepublish(records, resolver, publisher); Path resultsFile = Paths.get("publish-resolve-counts-" + LocalDateTime.now().withNano(0) + ".txt"); Files.write(resultsFile, recordCounts.stream() @@ -57,11 +57,11 @@ public static void main(String[] a) throws Exception { Files.write(resultsFile, fails.getBytes(), StandardOpenOption.APPEND); } } else { - keys = IntStream.range(0, keycount) + List keys = IntStream.range(0, keycount) .mapToObj(i -> Ed25519Kt.generateEd25519KeyPair().getFirst()) .collect(Collectors.toList()); long t0 = System.currentTimeMillis(); - List> futs = publish(keys, "The result".getBytes(), ipfs) + List> futs = publish(keys, "The result".getBytes(), publisher) .collect(Collectors.toList()); futs.forEach(res -> { try { @@ -76,7 +76,7 @@ public static void main(String[] a) throws Exception { long t1 = System.currentTimeMillis(); System.out.println("Published all in " + (t1-t0)/1000 + "s"); } - ipfs.stop().join(); + publisher.stop().join(); System.exit(0); } @@ -124,16 +124,18 @@ public static CompletableFuture publish(PublishResult signed, Emb }, ioExec); } - public static List resolveAndRepublish(List publishers, EmbeddedIpfs ipfs) { + public static List resolveAndRepublish(List publishers, + EmbeddedIpfs resolver, + EmbeddedIpfs publisher) { List res = new ArrayList<>(); int done = 0; for (PublishResult pub : publishers) { - List records = ipfs.resolveRecords(pub.priv.publicKey(), 30); + List records = resolver.resolveRecords(pub.priv.publicKey(), 30); res.add(records.size()); done++; if (done % 10 == 0) System.out.println("resolved " + done); - CompletableFuture.supplyAsync(() -> ipfs.publishPresignedRecord(pub.pub, pub.record)); + CompletableFuture.supplyAsync(() -> publisher.publishPresignedRecord(pub.pub, pub.record)); } return res; } From f7538797f87ae834312cb7c397df226803efd18a Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 27 Dec 2023 12:26:34 +0000 Subject: [PATCH 156/218] Try and remove joins in kademlia --- src/main/java/org/peergos/IpnsPublisher.java | 2 +- .../org/peergos/protocol/dht/Kademlia.java | 53 +++++++++++-------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index 8529f451..bea847d2 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -134,7 +134,7 @@ public static List resolveAndRepublish(List publishers, res.add(records.size()); done++; if (done % 10 == 0) - System.out.println("resolved " + done); + System.out.println("resolved " + res.stream().filter(c -> c > 0).count() + " / " + done); CompletableFuture.supplyAsync(() -> publisher.publishPresignedRecord(pub.pub, pub.record)); } return res; diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index f7127914..d76f8232 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -319,15 +319,15 @@ public CompletableFuture publishIpnsValue(PrivKey priv, return publishValue(publisher, signedRecord, us); } - private boolean putValue(Multihash publisher, - byte[] signedRecord, - PeerAddresses peer, - Host us) { + private CompletableFuture putValue(Multihash publisher, + byte[] signedRecord, + PeerAddresses peer, + Host us) { try { return dialPeer(peer, us).join() - .putValue(publisher, signedRecord).join(); + .putValue(publisher, signedRecord); } catch (Exception e) {} - return false; + return CompletableFuture.completedFuture(false); } private boolean hasTransportOverlap(PeerAddresses p) { @@ -378,10 +378,11 @@ public CompletableFuture publishValue(Multihash publisher, more.add(e); } } - ioExec.submit(() -> { - if (putValue(publisher, signedRecord, r.addresses, us)) - publishes.add(r.addresses.peerId); - }); + ioExec.submit(() -> putValue(publisher, signedRecord, r.addresses, us) + .thenAccept(done -> { + if (done) + publishes.add(r.addresses.peerId); + })); return more; }).join()); }) @@ -407,10 +408,11 @@ public CompletableFuture publishValue(Multihash publisher, .map(r -> { toQuery.remove(r); queried.add(r.addresses.peerId); - return ioExec.submit(() -> { - if (putValue(publisher, signedRecord, r.addresses, us)) - publishes.add(r.addresses.peerId); - }); + return ioExec.submit(() -> putValue(publisher, signedRecord, r.addresses, us) + .thenAccept(done -> { + if (done) + publishes.add(r.addresses.peerId); + })); }) .collect(Collectors.toList()); lastFutures.forEach(f -> { @@ -433,16 +435,21 @@ public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, return CompletableFuture.completedFuture(new String(records.get(records.size() - 1).value)); } - private Optional getValueFromPeer(PeerAddresses peer, Multihash publisher, Host us) { + private CompletableFuture> getValueFromPeer(PeerAddresses peer, Multihash publisher, Host us) { try { - return Optional.of(dialPeer(peer, us).orTimeout(1, TimeUnit.SECONDS).join() - .getValue(publisher).orTimeout(1, TimeUnit.SECONDS).join()); - } catch (Exception e) {} - return Optional.empty(); + return dialPeer(peer, us) + .orTimeout(1, TimeUnit.SECONDS) + .join() + .getValue(publisher) + .orTimeout(1, TimeUnit.SECONDS) + .thenApply(Optional::of); + } catch (Exception e) { + return CompletableFuture.completedFuture(Optional.empty()); + } } public List resolveValue(Multihash publisher, int minResults, Host us) { byte[] key = IPNS.getKey(publisher); - List candidates = new ArrayList<>(); + List candidates = Collections.synchronizedList(new ArrayList<>()); Optional local = engine.getRecord(publisher); local.ifPresent(candidates::add); @@ -465,8 +472,8 @@ public List resolveValue(Multihash publisher, int minResults, Host u .map(r -> { toQuery.remove(r); queried.add(r.addresses.peerId); - return ioExec.submit(() -> getValueFromPeer(r.addresses, publisher, us) - .ifPresent(g -> { + return ioExec.submit(() -> getValueFromPeer(r.addresses, publisher, us).thenAccept(get -> + get.ifPresent(g -> { if (g.record.isPresent() && g.record.get().publisher.equals(publisher)) candidates.add(g.record.get().value); for (PeerAddresses peer : g.closerPeers) { @@ -476,7 +483,7 @@ public List resolveValue(Multihash publisher, int minResults, Host u toQuery.add(e); } } - })); + }))); }) .collect(Collectors.toList()); futures.forEach(f -> { From 58cf06d65508ae5b90915471d1db8bd87693399c Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 29 Dec 2023 22:40:29 +1100 Subject: [PATCH 157/218] typo --- src/test/java/org/peergos/KademliaTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 8fd8d94e..6d81b010 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -72,7 +72,7 @@ public void ipnsBenchmark() throws Exception { node2.start().join(); IdentifyBuilder.addIdentifyProtocol(node2); - Cid value = blockstore1.put("Publiah me.".getBytes(), Cid.Codec.Raw).join(); + Cid value = blockstore1.put("Publish me.".getBytes(), Cid.Codec.Raw).join(); try { // bootstrap node 1 From 60a4a11f2b2c3a503171162528579b35703a02ce Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 7 Feb 2024 10:03:56 +0000 Subject: [PATCH 158/218] Only return our peerid (and no other peers) for find_node(us) These calls are used by go as live-ness checks --- .../peergos/protocol/dht/KademliaEngine.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 3908322e..62fe6c2d 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -49,12 +49,14 @@ public class KademliaEngine { public final Router router; private AddressBook addressBook; private final Multihash ourPeerId; + private final byte[] ourPeerIdBytes; private final Blockstore blocks; public KademliaEngine(Multihash ourPeerId, ProviderStore providersStore, RecordStore ipnsStore, Blockstore blocks) { this.providersStore = providersStore; this.ipnsStore = ipnsStore; this.ourPeerId = ourPeerId; + this.ourPeerIdBytes = ourPeerId.toBytes(); this.router = new Router(Id.create(ourPeerId.bareMultihash().toBytes(), 256), 2, 2, 2); this.blocks = blocks; } @@ -177,11 +179,16 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { Dht.Message.Builder builder = msg.toBuilder(); Multihash sourcePeer = Multihash.deserialize(source.getBytes()); byte[] target = msg.getKey().toByteArray(); - builder = builder.addAllCloserPeers(getKClosestPeers(target, BUCKET_SIZE) - .stream() - .filter(p -> ! p.peerId.equals(sourcePeer)) // don't tell a peer about themselves - .map(p -> p.toProtobuf(a -> isPublic(a))) - .collect(Collectors.toList())); + if (Arrays.equals(target, ourPeerIdBytes)) { + // Only return ourselves (without addresses) if they are querying for us + // This is because all Go peers query for this to check live-ness + builder = builder.addCloserPeers(new PeerAddresses(ourPeerId, Collections.emptyList()).toProtobuf()); + } else + builder = builder.addAllCloserPeers(getKClosestPeers(target, BUCKET_SIZE) + .stream() + .filter(p -> ! p.peerId.equals(sourcePeer)) // don't tell a peer about themselves + .map(p -> p.toProtobuf(a -> isPublic(a))) + .collect(Collectors.toList())); Dht.Message reply = builder.build(); stream.writeAndFlush(reply); responderSentBytes.inc(reply.getSerializedSize()); From 020f06c61c417a3c6c083ab19e9902fa6ac475f9 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 17 Feb 2024 11:57:54 +0000 Subject: [PATCH 159/218] Add optional bitswap protocol id to EmbeddedIpfs.build() --- src/main/java/org/peergos/EmbeddedIpfs.java | 5 +++-- src/main/java/org/peergos/IpnsPublisher.java | 2 +- src/main/java/org/peergos/Nabu.java | 3 ++- src/main/java/org/peergos/protocol/bitswap/Bitswap.java | 3 ++- src/test/java/org/peergos/EmbeddedIpfsTest.java | 3 ++- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 2dae613f..ba7dc597 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -216,7 +216,8 @@ public static EmbeddedIpfs build(RecordStore records, List bootstrap, IdentitySection identity, BlockRequestAuthoriser authoriser, - Optional handler) { + Optional handler, + Optional bitswapProtocolId) { Blockstore blockstore = provideBlocks ? new ProvidingBlockstore(blocks) : blocks; @@ -231,7 +232,7 @@ public static EmbeddedIpfs build(RecordStore records, Kademlia dht = new Kademlia(new KademliaEngine(ourPeerId, providers, records, blockstore), false); CircuitStopProtocol.Binding stop = new CircuitStopProtocol.Binding(); CircuitHopProtocol.RelayManager relayManager = CircuitHopProtocol.RelayManager.limitTo(builder.getPrivateKey(), ourPeerId, 5); - Bitswap bitswap = new Bitswap(new BitswapEngine(blockstore, authoriser, Bitswap.MAX_MESSAGE_SIZE, true)); + Bitswap bitswap = new Bitswap(bitswapProtocolId.orElse(Bitswap.PROTOCOL_ID), new BitswapEngine(blockstore, authoriser, Bitswap.MAX_MESSAGE_SIZE, true)); Optional httpHandler = handler.map(HttpProtocol.Binding::new); List protocols = new ArrayList<>(); diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index bea847d2..300b2b6b 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -147,7 +147,7 @@ public static EmbeddedIpfs startIpfs() { IdentitySection id = new IdentitySection(privKey.bytes(), peerId); EmbeddedIpfs ipfs = EmbeddedIpfs.build(new RamRecordStore(), new RamBlockstore(), false, List.of(new MultiAddress("/ip6/::/tcp/0")), Config.defaultBootstrapNodes, id, - (c, s, au) -> CompletableFuture.completedFuture(true), Optional.empty()); + (c, s, au) -> CompletableFuture.completedFuture(true), Optional.empty(), Optional.empty()); ipfs.start(); return ipfs; } diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index acbf69cc..349aec78 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -59,7 +59,8 @@ public Nabu(Args args) throws Exception { config.bootstrap.getBootstrapAddresses(), config.identity, authoriser, - config.addresses.proxyTargetAddress.map(Nabu::proxyHandler) + config.addresses.proxyTargetAddress.map(Nabu::proxyHandler), + Optional.empty() ); ipfs.start(); String apiAddressArg = "Addresses.API"; diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index 3c12fbb0..a993bb86 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -20,6 +20,7 @@ public class Bitswap extends StrictProtocolBinding implements AddressBookConsumer, ConnectionHandler { private static final Logger LOG = Logging.LOG(); public static int MAX_MESSAGE_SIZE = 2*1024*1024; + public static final String PROTOCOL_ID = "/ipfs/bitswap/1.2.0"; private final BitswapEngine engine; private final LRUCache connected = new LRUCache<>(100); @@ -27,7 +28,7 @@ public class Bitswap extends StrictProtocolBinding implements private AddressBook addrs; public Bitswap(BitswapEngine engine) { - super("/ipfs/bitswap/1.2.0", new BitswapProtocol(engine)); + super(PROTOCOL_ID, new BitswapProtocol(engine)); this.engine = engine; } diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index dab8a67d..82a6a395 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -124,6 +124,7 @@ public static EmbeddedIpfs build(List bootstrap, List Date: Sat, 17 Feb 2024 12:00:24 +0000 Subject: [PATCH 160/218] bump version to v0.7.2 --- pom.xml | 2 +- src/main/java/org/peergos/net/APIHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 08a25710..ecda4706 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.1 + v0.7.2 UTF-8 diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index 898f2559..a7a1c533 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -13,7 +13,7 @@ public class APIHandler extends Handler { public static final String API_URL = "/api/v0/"; - public static final Version CURRENT_VERSION = Version.parse("0.7.0"); + public static final Version CURRENT_VERSION = Version.parse("0.7.2"); private static final Logger LOG = Logging.LOG(); private static final boolean LOGGING = true; From a3c0e64113ee6b53f8cb4e565f90601e98447244 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 26 Feb 2024 16:13:53 +0000 Subject: [PATCH 161/218] make bitswap max message size configurable bump version to v0.7.3 --- pom.xml | 2 +- src/main/java/org/peergos/EmbeddedIpfs.java | 18 ++++++++++++++++-- .../protocol/bitswap/BitswapEngine.java | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index ecda4706..49950459 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.2 + v0.7.3 UTF-8 diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index ba7dc597..566f8a7a 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -209,6 +209,18 @@ public static Blockstore filteredBlockStore(Blockstore blocks, Config config) { } } + public static EmbeddedIpfs build(RecordStore records, + Blockstore blocks, + boolean provideBlocks, + List swarmAddresses, + List bootstrap, + IdentitySection identity, + BlockRequestAuthoriser authoriser, + Optional handler) { + return build(records, blocks, provideBlocks, swarmAddresses, bootstrap, identity, authoriser, handler, + Optional.empty(), Optional.empty()); + } + public static EmbeddedIpfs build(RecordStore records, Blockstore blocks, boolean provideBlocks, @@ -217,7 +229,8 @@ public static EmbeddedIpfs build(RecordStore records, IdentitySection identity, BlockRequestAuthoriser authoriser, Optional handler, - Optional bitswapProtocolId) { + Optional bitswapProtocolId, + Optional maxBitswapMsgSize) { Blockstore blockstore = provideBlocks ? new ProvidingBlockstore(blocks) : blocks; @@ -232,7 +245,8 @@ public static EmbeddedIpfs build(RecordStore records, Kademlia dht = new Kademlia(new KademliaEngine(ourPeerId, providers, records, blockstore), false); CircuitStopProtocol.Binding stop = new CircuitStopProtocol.Binding(); CircuitHopProtocol.RelayManager relayManager = CircuitHopProtocol.RelayManager.limitTo(builder.getPrivateKey(), ourPeerId, 5); - Bitswap bitswap = new Bitswap(bitswapProtocolId.orElse(Bitswap.PROTOCOL_ID), new BitswapEngine(blockstore, authoriser, Bitswap.MAX_MESSAGE_SIZE, true)); + Bitswap bitswap = new Bitswap(bitswapProtocolId.orElse(Bitswap.PROTOCOL_ID), + new BitswapEngine(blockstore, authoriser, maxBitswapMsgSize.orElse(Bitswap.MAX_MESSAGE_SIZE), true)); Optional httpHandler = handler.map(HttpProtocol.Binding::new); List protocols = new ArrayList<>(); diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 0eb87f27..381a2f60 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -333,7 +333,7 @@ public void buildAndSendMessages(List for (int i=0; i < blocks.size(); i++) { MessageOuterClass.Message.Block block = blocks.get(i); int blockSize = block.getSerializedSize(); - if (blockSize + messageSize > Bitswap.MAX_MESSAGE_SIZE) { + if (blockSize + messageSize > maxMessageSize) { sender.accept(builder.build()); builder = MessageOuterClass.Message.newBuilder(); messageSize = 0; From 189cd7af490d145dafab06b98e4c11f361585c3e Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 26 Feb 2024 16:18:54 +0000 Subject: [PATCH 162/218] fix calls to embeddedipfs.build --- src/main/java/org/peergos/IpnsPublisher.java | 2 +- src/main/java/org/peergos/Nabu.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index 300b2b6b..bea847d2 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -147,7 +147,7 @@ public static EmbeddedIpfs startIpfs() { IdentitySection id = new IdentitySection(privKey.bytes(), peerId); EmbeddedIpfs ipfs = EmbeddedIpfs.build(new RamRecordStore(), new RamBlockstore(), false, List.of(new MultiAddress("/ip6/::/tcp/0")), Config.defaultBootstrapNodes, id, - (c, s, au) -> CompletableFuture.completedFuture(true), Optional.empty(), Optional.empty()); + (c, s, au) -> CompletableFuture.completedFuture(true), Optional.empty()); ipfs.start(); return ipfs; } diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index 349aec78..acbf69cc 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -59,8 +59,7 @@ public Nabu(Args args) throws Exception { config.bootstrap.getBootstrapAddresses(), config.identity, authoriser, - config.addresses.proxyTargetAddress.map(Nabu::proxyHandler), - Optional.empty() + config.addresses.proxyTargetAddress.map(Nabu::proxyHandler) ); ipfs.start(); String apiAddressArg = "Addresses.API"; From 3411741d74a6f8924062f824d5e9fe88461aae9a Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 26 Feb 2024 16:19:25 +0000 Subject: [PATCH 163/218] version v0.7.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49950459..87869c52 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.3 + v0.7.4 UTF-8 From 4152847c01d0656ad0df46ccd9938a1f3e963977 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 26 Feb 2024 16:24:38 +0000 Subject: [PATCH 164/218] fix test --- src/test/java/org/peergos/EmbeddedIpfsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 82a6a395..d1457f6b 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -125,6 +125,6 @@ public static EmbeddedIpfs build(List bootstrap, List Date: Mon, 26 Feb 2024 16:43:00 +0000 Subject: [PATCH 165/218] ignore non unit test --- src/test/java/org/peergos/FindPeerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/peergos/FindPeerTest.java b/src/test/java/org/peergos/FindPeerTest.java index 51173c18..18d71d57 100644 --- a/src/test/java/org/peergos/FindPeerTest.java +++ b/src/test/java/org/peergos/FindPeerTest.java @@ -13,6 +13,7 @@ public class FindPeerTest { + @Ignore @Test public void findLongRunningNode() { RamBlockstore blockstore1 = new RamBlockstore(); From 392d59004db150679694161c7b514dab712c1c9d Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 26 Feb 2024 20:44:32 +0000 Subject: [PATCH 166/218] correctly handle max block size in api --- src/main/java/org/peergos/EmbeddedIpfs.java | 4 ++++ src/main/java/org/peergos/net/APIHandler.java | 4 +++- src/main/java/org/peergos/protocol/bitswap/Bitswap.java | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 566f8a7a..708283b1 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -70,6 +70,10 @@ public EmbeddedIpfs(Host node, () -> blockstore.refs(false).join().stream(), node, dht, q)); } + public int maxBlockSize() { + return bitswap.maxBlockSize(); + } + public List getBlocks(List wants, Set peers, boolean addToLocal) { List blocksFound = new ArrayList<>(); diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index a7a1c533..2e8f7199 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -31,9 +31,11 @@ public class APIHandler extends Handler { public static final String FIND_PROVS = "dht/findprovs"; private final EmbeddedIpfs ipfs; + private final int maxBlockSize; public APIHandler(EmbeddedIpfs ipfs) { this.ipfs = ipfs; + this.maxBlockSize = ipfs.maxBlockSize(); } public void handleCallToAPI(HttpExchange httpExchange) { @@ -109,7 +111,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { throw new APIException("Multiple input not supported"); } byte[] block = data.get(0); - if (block.length > 1024 * 1024 * 2) { //todo what should the limit be? + if (block.length > maxBlockSize) { throw new APIException("Block too large"); } Cid cid = ipfs.blockstore.put(block, Cid.Codec.lookupIPLDName(reqFormat)).join(); diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index a993bb86..d3448ab9 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -42,6 +42,10 @@ public void setAddressBook(AddressBook addrs) { this.addrs = addrs; } + public int maxBlockSize() { + return engine.maxMessageSize(); + } + @Override public void handleConnection(@NotNull Connection connection) { // add all outgoing connections to an LRU of candidates From 91afed8803dc259ed3e5cef1d57f4bbc9bf05c65 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 26 Feb 2024 21:07:20 +0000 Subject: [PATCH 167/218] Fix tests --- src/test/java/org/peergos/HandlerTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/peergos/HandlerTest.java b/src/test/java/org/peergos/HandlerTest.java index 53c3e57e..3aed6452 100644 --- a/src/test/java/org/peergos/HandlerTest.java +++ b/src/test/java/org/peergos/HandlerTest.java @@ -12,6 +12,7 @@ import org.peergos.blockstore.*; import org.peergos.client.NabuClient; import org.peergos.net.APIHandler; +import org.peergos.protocol.bitswap.*; import org.peergos.protocol.dht.Kademlia; import org.peergos.protocol.dht.RamProviderStore; import org.peergos.protocol.dht.RamRecordStore; @@ -45,7 +46,7 @@ public void codecTest() { apiServer = HttpServer.create(localAPIAddress, 500); Blockstore blocks = new TypeLimitedBlockstore(new RamBlockstore(), Set.of(Cid.Codec.Raw)); EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, null, - null, Optional.empty(), Collections.emptyList(), Optional.empty()); + new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); @@ -124,7 +125,8 @@ public void blockMethodsTest() { InetSocketAddress localAPIAddress = new InetSocketAddress(apiAddress.getHost(), apiAddress.getPort()); apiServer = HttpServer.create(localAPIAddress, 500); - EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, null, null, Optional.empty(), Collections.emptyList(), Optional.empty()); + EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, + null, new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); From dca07ba2b53bbac875cfdbe86d7f762941dda634 Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 27 Feb 2024 09:28:47 +0000 Subject: [PATCH 168/218] Increase max http body size --- src/main/java/org/peergos/protocol/http/HttpProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/http/HttpProtocol.java b/src/main/java/org/peergos/protocol/http/HttpProtocol.java index b733345e..217c5f43 100644 --- a/src/main/java/org/peergos/protocol/http/HttpProtocol.java +++ b/src/main/java/org/peergos/protocol/http/HttpProtocol.java @@ -19,7 +19,7 @@ import java.util.function.*; public class HttpProtocol extends ProtocolHandler { - private static final int MAX_BODY_SIZE = 2 * 1024 * 1024; // TODO remove this and make it fully streaming + private static final int MAX_BODY_SIZE = 8 * 1024 * 1024; // TODO remove this and make it fully streaming public static class Binding extends StrictProtocolBinding { public Binding(SocketAddress proxyTarget) { From 503edf80fd3e83e7e437e1722778472fb37844e6 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 1 Mar 2024 13:53:56 +0000 Subject: [PATCH 169/218] Save ipns records to file at beginning in ipns publisher Also use larger (1kib) values --- src/main/java/org/peergos/IpnsPublisher.java | 26 +++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index bea847d2..da00a42b 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -60,19 +60,12 @@ public static void main(String[] a) throws Exception { List keys = IntStream.range(0, keycount) .mapToObj(i -> Ed25519Kt.generateEd25519KeyPair().getFirst()) .collect(Collectors.toList()); + byte[] value = new byte[1024]; + new Random(28).nextBytes(value); long t0 = System.currentTimeMillis(); - List> futs = publish(keys, "The result".getBytes(), publisher) + List> futs = publish(keys, value, publisher, publishFile) .collect(Collectors.toList()); - futs.forEach(res -> { - try { - Files.write(publishFile, res.join().toString().getBytes(), - publishFile.toFile().exists() ? - StandardOpenOption.APPEND : - StandardOpenOption.CREATE_NEW); - } catch (IOException e) { - e.printStackTrace(); - } - }); + futs.forEach(res -> res.join()); long t1 = System.currentTimeMillis(); System.out.println("Published all in " + (t1-t0)/1000 + "s"); } @@ -100,7 +93,10 @@ public String toString() { } } - public static Stream> publish(List publishers, byte[] value, EmbeddedIpfs ipfs) throws IOException { + public static Stream> publish(List publishers, + byte[] value, + EmbeddedIpfs ipfs, + Path publishFile) throws IOException { LocalDateTime expiry = LocalDateTime.now().plusDays(7); long ttlNanos = 7L * 24 * 3600 * 1000_000_000; List signed = publishers.stream() @@ -109,6 +105,12 @@ public static Stream> publish(List pub byte[] record = IPNS.createSignedRecord(value, expiry, 1, ttlNanos, p); return new PublishResult(p, pub, record, 0); }).collect(Collectors.toList()); + for (PublishResult rec : signed) { + Files.write(publishFile, rec.toString().getBytes(), + publishFile.toFile().exists() ? + StandardOpenOption.APPEND : + StandardOpenOption.CREATE_NEW); + } return publish(signed, ipfs); } From 49e55b33079c39d79fbe424a31d1cf9e199fbf53 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 1 Mar 2024 14:35:18 +0000 Subject: [PATCH 170/218] log publish progress in ipns publisher --- src/main/java/org/peergos/IpnsPublisher.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index da00a42b..b3baee37 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -65,7 +65,13 @@ public static void main(String[] a) throws Exception { long t0 = System.currentTimeMillis(); List> futs = publish(keys, value, publisher, publishFile) .collect(Collectors.toList()); - futs.forEach(res -> res.join()); + int done = 0; + for (CompletableFuture fut : futs) { + fut.join(); + done++; + if (done % 10 == 0) + System.out.println("published " + done + " / " + keycount); + } long t1 = System.currentTimeMillis(); System.out.println("Published all in " + (t1-t0)/1000 + "s"); } From df7304d28769bf81c0790258a5ffc7d85cb6e3c8 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 1 Mar 2024 20:03:23 +0000 Subject: [PATCH 171/218] Further parallelise ipns republisher --- src/main/java/org/peergos/IpnsPublisher.java | 33 +++++++++++--------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index b3baee37..c1b7aedf 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -16,15 +16,15 @@ import java.time.*; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.*; import java.util.stream.*; import java.util.stream.Stream; public class IpnsPublisher { - private static final ExecutorService ioExec = Executors.newFixedThreadPool(10); + private static final ExecutorService ioExec = Executors.newFixedThreadPool(50); public static void main(String[] a) throws Exception { Path publishFile = Paths.get("publishers.txt"); int keycount = 1000; - EmbeddedIpfs publisher = startIpfs(); EmbeddedIpfs resolver = startIpfs(); if (publishFile.toFile().exists()) { List lines = Files.readAllLines(publishFile); @@ -38,7 +38,7 @@ public static void main(String[] a) throws Exception { System.out.println("Resolving " + records.size() + " keys"); for (int c=0; c < 100; c++) { long t0 = System.currentTimeMillis(); - List recordCounts = resolveAndRepublish(records, resolver, publisher); + List recordCounts = resolveAndRepublish(records, resolver, resolver); Path resultsFile = Paths.get("publish-resolve-counts-" + LocalDateTime.now().withNano(0) + ".txt"); Files.write(resultsFile, recordCounts.stream() @@ -56,12 +56,14 @@ public static void main(String[] a) throws Exception { System.out.println(fails); Files.write(resultsFile, fails.getBytes(), StandardOpenOption.APPEND); } + resolver.stop().join(); } else { List keys = IntStream.range(0, keycount) .mapToObj(i -> Ed25519Kt.generateEd25519KeyPair().getFirst()) .collect(Collectors.toList()); byte[] value = new byte[1024]; new Random(28).nextBytes(value); + EmbeddedIpfs publisher = startIpfs(); long t0 = System.currentTimeMillis(); List> futs = publish(keys, value, publisher, publishFile) .collect(Collectors.toList()); @@ -74,8 +76,8 @@ public static void main(String[] a) throws Exception { } long t1 = System.currentTimeMillis(); System.out.println("Published all in " + (t1-t0)/1000 + "s"); + publisher.stop().join(); } - publisher.stop().join(); System.exit(0); } @@ -135,17 +137,20 @@ public static CompletableFuture publish(PublishResult signed, Emb public static List resolveAndRepublish(List publishers, EmbeddedIpfs resolver, EmbeddedIpfs publisher) { - List res = new ArrayList<>(); - int done = 0; - for (PublishResult pub : publishers) { + AtomicInteger done = new AtomicInteger(0); + AtomicInteger successes = new AtomicInteger(0); + List> futs = publishers.stream().map(pub -> CompletableFuture.supplyAsync(() -> { List records = resolver.resolveRecords(pub.priv.publicKey(), 30); - res.add(records.size()); - done++; - if (done % 10 == 0) - System.out.println("resolved " + res.stream().filter(c -> c > 0).count() + " / " + done); - CompletableFuture.supplyAsync(() -> publisher.publishPresignedRecord(pub.pub, pub.record)); - } - return res; + int success = records.isEmpty() ? successes.get() : successes.incrementAndGet(); + int total = done.incrementAndGet(); + if (total % 10 == 0) + System.out.println("resolved " + success + " / " + done); + publisher.publishPresignedRecord(pub.pub, pub.record).join(); + return records.size(); + }, ioExec)).collect(Collectors.toList()); + return futs.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); } public static EmbeddedIpfs startIpfs() { From 0cbf479355f5466294c7d8175d917f1fa34eae33 Mon Sep 17 00:00:00 2001 From: Pedro Pietroluongo Date: Fri, 1 Mar 2024 21:49:22 -0300 Subject: [PATCH 172/218] Upgrade nabu version in docker file --- Containerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Containerfile b/Containerfile index 38f344fa..28f3f11d 100644 --- a/Containerfile +++ b/Containerfile @@ -18,6 +18,6 @@ WORKDIR /opt/nabu COPY --from=build /opt/nabu/target /opt/nabu -ENTRYPOINT ["java", "-cp", "/opt/nabu/nabu-v0.6.0-jar-with-dependencies.jar", "org.peergos.client.InteropTestClient"] +ENTRYPOINT ["java", "-cp", "/opt/nabu/nabu-v0.7.4-jar-with-dependencies.jar", "org.peergos.client.InteropTestClient"] EXPOSE 4001 From 66527d9ea08027e8aea204306e5e1f31e087ac02 Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 3 Mar 2024 00:01:03 +0000 Subject: [PATCH 173/218] Fix hangs in ipns republisher --- src/main/java/org/peergos/IpnsPublisher.java | 21 +++++++----- .../org/peergos/protocol/dht/Kademlia.java | 33 ++++++++++--------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index c1b7aedf..a5360f17 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -24,7 +24,6 @@ public class IpnsPublisher { private static final ExecutorService ioExec = Executors.newFixedThreadPool(50); public static void main(String[] a) throws Exception { Path publishFile = Paths.get("publishers.txt"); - int keycount = 1000; EmbeddedIpfs resolver = startIpfs(); if (publishFile.toFile().exists()) { List lines = Files.readAllLines(publishFile); @@ -58,6 +57,7 @@ public static void main(String[] a) throws Exception { } resolver.stop().join(); } else { + int keycount = 1000; List keys = IntStream.range(0, keycount) .mapToObj(i -> Ed25519Kt.generateEd25519KeyPair().getFirst()) .collect(Collectors.toList()); @@ -140,13 +140,18 @@ public static List resolveAndRepublish(List publishers, AtomicInteger done = new AtomicInteger(0); AtomicInteger successes = new AtomicInteger(0); List> futs = publishers.stream().map(pub -> CompletableFuture.supplyAsync(() -> { - List records = resolver.resolveRecords(pub.priv.publicKey(), 30); - int success = records.isEmpty() ? successes.get() : successes.incrementAndGet(); - int total = done.incrementAndGet(); - if (total % 10 == 0) - System.out.println("resolved " + success + " / " + done); - publisher.publishPresignedRecord(pub.pub, pub.record).join(); - return records.size(); + try { + List records = resolver.resolveRecords(pub.priv.publicKey(), 30); + int success = records.isEmpty() ? successes.get() : successes.incrementAndGet(); + int total = done.incrementAndGet(); + if (total % 10 == 0) + System.out.println("resolved " + success + " / " + done); + publisher.publishPresignedRecord(pub.pub, pub.record).join(); + return records.size(); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } }, ioExec)).collect(Collectors.toList()); return futs.stream() .map(CompletableFuture::join) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index d76f8232..9a736327 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -365,11 +365,11 @@ public CompletableFuture publishValue(Multihash publisher, .filter(r -> hasTransportOverlap(r.addresses)) // don't waste time trying to dial nodes we can't .limit(queryParallelism) .collect(Collectors.toList()); - List>> futures = thisRound.stream() + List>> futures = thisRound.stream() .map(r -> { toQuery.remove(r); queried.add(r.addresses.peerId); - return ioExec.submit(() -> getCloserPeers(key, r.addresses, us).thenApply(res -> { + return CompletableFuture.supplyAsync(() -> getCloserPeers(key, r.addresses, us).thenApply(res -> { List more = new ArrayList<>(); for (PeerAddresses peer : res) { if (! queried.contains(peer.peerId)) { @@ -378,20 +378,20 @@ public CompletableFuture publishValue(Multihash publisher, more.add(e); } } - ioExec.submit(() -> putValue(publisher, signedRecord, r.addresses, us) + CompletableFuture.supplyAsync(() -> putValue(publisher, signedRecord, r.addresses, us) .thenAccept(done -> { if (done) publishes.add(r.addresses.peerId); - })); + }), ioExec); return more; - }).join()); + }).join(), ioExec); }) .collect(Collectors.toList()); futures.forEach(f -> { try { if (publishes.size() >= minPublishes) return; - toQuery.addAll(f.get()); + toQuery.addAll(f.orTimeout(2, TimeUnit.SECONDS).join()); } catch (Exception e) {} }); // exit early if we have enough results @@ -400,24 +400,24 @@ public CompletableFuture publishValue(Multihash publisher, if (toQuery.size() == remaining) { // publish to closest remaining nodes System.out.println("Publishing to further nodes, so far only " + publishes.size()); - while (publishes.size() < minPublishes) { + while (publishes.size() < minPublishes && !toQuery.isEmpty()) { List closest = toQuery.stream() .limit(minPublishes - publishes.size() + 5) .collect(Collectors.toList()); - List> lastFutures = closest.stream() + List> lastFutures = closest.stream() .map(r -> { toQuery.remove(r); queried.add(r.addresses.peerId); - return ioExec.submit(() -> putValue(publisher, signedRecord, r.addresses, us) + return CompletableFuture.supplyAsync(() -> putValue(publisher, signedRecord, r.addresses, us) .thenAccept(done -> { if (done) publishes.add(r.addresses.peerId); - })); + }), ioExec); }) .collect(Collectors.toList()); lastFutures.forEach(f -> { try { - f.get(); + f.orTimeout(2, TimeUnit.SECONDS).join(); } catch (Exception e) {} }); } @@ -468,29 +468,30 @@ public List resolveValue(Multihash publisher, int minResults, Host u List thisRound = toQuery.stream() .limit(queryParallelism) .collect(Collectors.toList()); - List> futures = thisRound.stream() + List>> futures = thisRound.stream() .map(r -> { toQuery.remove(r); queried.add(r.addresses.peerId); - return ioExec.submit(() -> getValueFromPeer(r.addresses, publisher, us).thenAccept(get -> + return CompletableFuture.supplyAsync(() -> getValueFromPeer(r.addresses, publisher, us).thenAccept(get -> get.ifPresent(g -> { if (g.record.isPresent() && g.record.get().publisher.equals(publisher)) candidates.add(g.record.get().value); for (PeerAddresses peer : g.closerPeers) { - if (! queried.contains(peer.peerId) && hasTransportOverlap(peer)) { + if (!queried.contains(peer.peerId) && hasTransportOverlap(peer)) { Id peerKey = Id.create(Hash.sha256(IPNS.getKey(peer.peerId)), 256); RoutingEntry e = new RoutingEntry(peerKey, peer); toQuery.add(e); } } - }))); + })), ioExec); }) .collect(Collectors.toList()); futures.forEach(f -> { try { if (candidates.size() >= minResults) return; - f.get(); + f.orTimeout(5, TimeUnit.SECONDS).join() + .orTimeout(5, TimeUnit.SECONDS).join(); } catch (Exception e) {} }); // exit early if we have enough results From 7bc1975bd3a81b87f538ce9fc6209585a2be4fd8 Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 3 Mar 2024 19:27:21 +0000 Subject: [PATCH 174/218] Revert to using separate publisher and resolver in IpnsPublisher --- src/main/java/org/peergos/IpnsPublisher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index a5360f17..0035428b 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -24,8 +24,9 @@ public class IpnsPublisher { private static final ExecutorService ioExec = Executors.newFixedThreadPool(50); public static void main(String[] a) throws Exception { Path publishFile = Paths.get("publishers.txt"); - EmbeddedIpfs resolver = startIpfs(); + EmbeddedIpfs publisher = startIpfs(); if (publishFile.toFile().exists()) { + EmbeddedIpfs resolver = startIpfs(); List lines = Files.readAllLines(publishFile); List records = lines.stream() .map(line -> new PublishResult(KeyKt.unmarshalPrivateKey(ArrayOps.hexToBytes(line.split(" ")[0])), @@ -37,7 +38,7 @@ public static void main(String[] a) throws Exception { System.out.println("Resolving " + records.size() + " keys"); for (int c=0; c < 100; c++) { long t0 = System.currentTimeMillis(); - List recordCounts = resolveAndRepublish(records, resolver, resolver); + List recordCounts = resolveAndRepublish(records, resolver, publisher); Path resultsFile = Paths.get("publish-resolve-counts-" + LocalDateTime.now().withNano(0) + ".txt"); Files.write(resultsFile, recordCounts.stream() @@ -63,7 +64,6 @@ public static void main(String[] a) throws Exception { .collect(Collectors.toList()); byte[] value = new byte[1024]; new Random(28).nextBytes(value); - EmbeddedIpfs publisher = startIpfs(); long t0 = System.currentTimeMillis(); List> futs = publish(keys, value, publisher, publishFile) .collect(Collectors.toList()); From e7452b8d64c7815ba98883f2421f5e4222b319b7 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 13 Mar 2024 17:31:31 +0000 Subject: [PATCH 175/218] Stop kademlia bootstrap thread when node is stopped --- src/main/java/org/peergos/EmbeddedIpfs.java | 1 + src/main/java/org/peergos/protocol/dht/Kademlia.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 708283b1..9cd7a72e 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -160,6 +160,7 @@ public CompletableFuture stop() throws Exception { records.close(); } blockProvider.ifPresent(b -> b.stop()); + dht.stopBootstrapThread(); return node != null ? node.stop() : CompletableFuture.completedFuture(null); } diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 9a736327..17a39e8a 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -18,6 +18,7 @@ import java.time.*; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.*; import java.util.function.*; import java.util.logging.*; import java.util.stream.*; @@ -73,9 +74,11 @@ public int bootstrapRoutingTable(Host host, List addrs, Predicate< return successes; } + private AtomicBoolean running = new AtomicBoolean(false); public void startBootstrapThread(Host us) { + running.set(true); new Thread(() -> { - while (true) { + while (running.get()) { try { bootstrap(us); Thread.sleep(BOOTSTRAP_PERIOD_MILLIS); @@ -86,6 +89,10 @@ public void startBootstrapThread(Host us) { }, "Kademlia bootstrap").start(); } + public void stopBootstrapThread() { + running.set(false); + } + private boolean connectTo(Host us, PeerAddresses peer) { try { new Identify().dial(us, PeerId.fromBase58(peer.peerId.toBase58()), getPublic(peer)).getController().join().id().join(); From 6201d725bd0a4c4a5ddbeb681f75bb0be2be7fbf Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 21 Mar 2024 15:55:10 +0000 Subject: [PATCH 176/218] Add new API calls for getting ipns records, and bulk block deletion --- .../java/org/peergos/AggregatedMetrics.java | 2 + src/main/java/org/peergos/net/APIHandler.java | 69 ++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/AggregatedMetrics.java b/src/main/java/org/peergos/AggregatedMetrics.java index b61f58fe..3087c76f 100644 --- a/src/main/java/org/peergos/AggregatedMetrics.java +++ b/src/main/java/org/peergos/AggregatedMetrics.java @@ -23,11 +23,13 @@ private static Counter build(String name, String help) { public static final Counter API_BLOCK_GET = build("api_block_get", "Total calls to block/get."); public static final Counter API_BLOCK_PUT = build("api_block_put", "Total calls to block/put."); public static final Counter API_BLOCK_RM = build("api_block_rm", "Total calls to block/rm."); + public static final Counter API_BLOCK_RM_BULK = build("api_block_rm_bulk", "Total calls to block/rm/bulk."); public static final Counter API_BLOCK_STAT = build("api_block_stat", "Total calls to block/stat."); public static final Counter API_REFS_LOCAL = build("api_refs_local", "Total calls to refs/local."); public static final Counter API_BLOCK_HAS = build("api_block_has", "Total calls to block/has."); public static final Counter API_BLOOM_ADD = build("api_bloom_add", "Total calls to bloom/add."); public static final Counter API_FIND_PROVS = build("api_dht_findprovs", "Total calls to dht/findprovs."); + public static final Counter API_IPNS_GET = build("api_ipns_get", "Total calls to ipns/get."); public static void startExporter(String address, int port) throws IOException { LOG.info("Starting metrics server at " + address + ":" + port); diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index 2e8f7199..9fb5dbd0 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -1,12 +1,17 @@ package org.peergos.net; import io.ipfs.cid.Cid; +import io.ipfs.multihash.*; import io.libp2p.core.PeerId; +import io.libp2p.crypto.keys.*; import org.peergos.*; +import org.peergos.protocol.ipns.*; +import org.peergos.protocol.ipns.pb.*; import org.peergos.util.*; import com.sun.net.httpserver.HttpExchange; -import java.io.IOException; +import java.io.*; +import java.nio.*; import java.util.*; import java.util.logging.Logger; import java.util.stream.*; @@ -23,12 +28,14 @@ public class APIHandler extends Handler { public static final String GET = "block/get"; public static final String PUT = "block/put"; public static final String RM = "block/rm"; + public static final String RM_BULK = "block/rm/bulk"; public static final String STAT = "block/stat"; public static final String REFS_LOCAL = "refs/local"; public static final String BLOOM_ADD = "bloom/add"; public static final String HAS = "block/has"; public static final String FIND_PROVS = "dht/findprovs"; + public static final String IPNS_GET = "ipns/get"; private final EmbeddedIpfs ipfs; private final int maxBlockSize; @@ -141,6 +148,31 @@ public void handleCallToAPI(HttpExchange httpExchange) { } break; } + case RM_BULK: { + AggregatedMetrics.API_BLOCK_RM_BULK.inc(); + Map json = (Map) JSONParser.parse(new String(readFully(httpExchange.getRequestBody()))); + List cids = ((List)json.get("cids")) + .stream() + .map(Cid::decode) + .collect(Collectors.toList()); + boolean deleted = true; + for (Cid cid : cids) { + deleted &= ipfs.blockstore.rm(cid).join(); + } + + if (deleted) { + Map res = new HashMap<>(); + res.put("Res", "true"); + replyJson(httpExchange, JSONParser.toString(res)); + } else { + try { + httpExchange.sendResponseHeaders(400, 0); + } catch (IOException ioe) { + HttpUtil.replyError(httpExchange, ioe); + } + } + break; + } case STAT: { // https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-block-stat AggregatedMetrics.API_BLOCK_STAT.inc(); if (args == null || args.size() != 1) { @@ -217,6 +249,31 @@ public void handleCallToAPI(HttpExchange httpExchange) { replyBytes(httpExchange, sb.toString().getBytes()); break; } + case IPNS_GET: { + AggregatedMetrics.API_IPNS_GET.inc(); + if (args == null || args.size() != 1) { + throw new APIException("argument \"signer\" is required"); + } + Multihash signer = Multihash.fromBase58(args.get(0)); + if (signer.getType() != Multihash.Type.id) + throw new IllegalStateException("Can only resolve Ed25519 ipns mappings"); + byte[] pubKeymaterial = Arrays.copyOfRange(signer.getHash(), 4, 36); + io.libp2p.core.crypto.PubKey pub = new Ed25519PublicKey(new org.bouncycastle.crypto.params.Ed25519PublicKeyParameters(pubKeymaterial, 0)); + List records = ipfs.resolveRecords(pub, 1) + .stream() + .sorted() + .collect(Collectors.toList()); + if (records.isEmpty()) + throw new IllegalStateException("Couldn't resolve " + signer); + IpnsRecord latest = records.get(records.size() - 1); + Ipns.IpnsEntry entry = Ipns.IpnsEntry.parseFrom(ByteBuffer.wrap(latest.raw)); + Map res = new HashMap<>(); + res.put("sig", ArrayOps.bytesToHex(entry.getSignatureV2().toByteArray())); + res.put("data", ArrayOps.bytesToHex(entry.getData().toByteArray())); + String json = JSONParser.toString(res); + replyJson(httpExchange, json); + break; + } default: { httpExchange.sendResponseHeaders(404, 0); break; @@ -231,4 +288,14 @@ public void handleCallToAPI(HttpExchange httpExchange) { LOG.info("API Handler handled " + path + " query in: " + (t2 - t1) + " mS"); } } + + private byte[] readFully(InputStream in) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] b = new byte[0x1000]; + int nRead; + while ((nRead = in.read(b, 0, b.length)) != -1 ) + bout.write(b, 0, nRead); + in.close(); + return bout.toByteArray(); + } } From 0834816013b95df6b4b62c9d4fc8adde72a9399a Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 22 Mar 2024 08:11:19 +0000 Subject: [PATCH 177/218] bump version to v0.7.5 --- pom.xml | 2 +- src/main/java/org/peergos/net/APIHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 87869c52..9d30325e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.4 + v0.7.5 UTF-8 diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index 9fb5dbd0..e8761b8d 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -18,7 +18,7 @@ public class APIHandler extends Handler { public static final String API_URL = "/api/v0/"; - public static final Version CURRENT_VERSION = Version.parse("0.7.2"); + public static final Version CURRENT_VERSION = Version.parse("0.7.5"); private static final Logger LOG = Logging.LOG(); private static final boolean LOGGING = true; From 85bd5b9bd176feb6da38b56993375db879f87986 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 27 Mar 2024 19:57:30 +0000 Subject: [PATCH 178/218] Update h2 version to support native-image --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9d30325e..f0250196 100644 --- a/pom.xml +++ b/pom.xml @@ -196,7 +196,7 @@ com.h2database h2 - 2.1.214 + 2.2.224 io.prometheus From 6a54b41e63c800760502a59c5e2eeb167e977a4d Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 27 Mar 2024 19:58:49 +0000 Subject: [PATCH 179/218] bump version to v0.7.6 --- pom.xml | 2 +- src/main/java/org/peergos/net/APIHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f0250196..db3daea5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.5 + v0.7.6 UTF-8 diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index e8761b8d..b45c5d13 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -18,7 +18,7 @@ public class APIHandler extends Handler { public static final String API_URL = "/api/v0/"; - public static final Version CURRENT_VERSION = Version.parse("0.7.5"); + public static final Version CURRENT_VERSION = Version.parse("0.7.6"); private static final Logger LOG = Logging.LOG(); private static final boolean LOGGING = true; From c92a1fca4d41e09efae788eae3dc780815781c4d Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 27 Mar 2024 22:46:45 +0000 Subject: [PATCH 180/218] Update netty-codec-http2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index db3daea5..06e6569d 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,7 @@ io.netty netty-codec-http2 - 4.1.90.Final + 4.1.108.Final org.apache.commons From 774d01c25e7721f6172a07d89c2a3b39d6f751ea Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Mar 2024 07:17:48 +0000 Subject: [PATCH 181/218] Solve h2 db version conflict --- src/main/java/org/peergos/Nabu.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/Nabu.java b/src/main/java/org/peergos/Nabu.java index acbf69cc..c3698075 100644 --- a/src/main/java/org/peergos/Nabu.java +++ b/src/main/java/org/peergos/Nabu.java @@ -49,7 +49,7 @@ public Nabu(Args args) throws Exception { LOG.info("Starting Nabu version: " + APIHandler.CURRENT_VERSION); BlockRequestAuthoriser authoriser = (c, p, a) -> CompletableFuture.completedFuture(true); - Path datastorePath = ipfsPath.resolve("datastore").resolve("h2.datastore"); + Path datastorePath = ipfsPath.resolve("datastore").resolve("h2-v2.datastore"); DatabaseRecordStore records = new DatabaseRecordStore(datastorePath.toAbsolutePath().toString()); BlockMetadataStore meta = buildBlockMetadata(args); EmbeddedIpfs ipfs = EmbeddedIpfs.build(records, From 79b9f2a3be2bca69ba8cd7172a81c738d2fa55a4 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Mar 2024 10:50:24 +0000 Subject: [PATCH 182/218] Update netty via jvm-libp2p --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 06e6569d..be66de19 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ com.github.peergos jvm-libp2p - 0.16.3 + 0.16.6 org.bouncycastle From d99451b8d860a4d4929275e95f5d8012b0a35574 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 28 Mar 2024 12:23:24 +0000 Subject: [PATCH 183/218] Support resolving rsa ipns mappings --- src/main/java/org/peergos/EmbeddedIpfs.java | 3 +-- src/main/java/org/peergos/IpnsPublisher.java | 2 +- src/main/java/org/peergos/net/APIHandler.java | 6 +----- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 9cd7a72e..3d22a8f9 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -126,8 +126,7 @@ public CompletableFuture resolveValue(PubKey pub, int minResults) { return CompletableFuture.completedFuture(records.get(records.size() - 1).value); } - public List resolveRecords(PubKey pub, int minResults) { - Multihash publisher = Multihash.deserialize(PeerId.fromPubKey(pub).getBytes()); + public List resolveRecords(Multihash publisher, int minResults) { List candidates = dht.resolveValue(publisher, minResults, node); return candidates.stream().sorted().collect(Collectors.toList()); } diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index 0035428b..c1741ed2 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -141,7 +141,7 @@ public static List resolveAndRepublish(List publishers, AtomicInteger successes = new AtomicInteger(0); List> futs = publishers.stream().map(pub -> CompletableFuture.supplyAsync(() -> { try { - List records = resolver.resolveRecords(pub.priv.publicKey(), 30); + List records = resolver.resolveRecords(pub.pub, 30); int success = records.isEmpty() ? successes.get() : successes.incrementAndGet(); int total = done.incrementAndGet(); if (total % 10 == 0) diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index b45c5d13..a1e6af40 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -255,11 +255,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { throw new APIException("argument \"signer\" is required"); } Multihash signer = Multihash.fromBase58(args.get(0)); - if (signer.getType() != Multihash.Type.id) - throw new IllegalStateException("Can only resolve Ed25519 ipns mappings"); - byte[] pubKeymaterial = Arrays.copyOfRange(signer.getHash(), 4, 36); - io.libp2p.core.crypto.PubKey pub = new Ed25519PublicKey(new org.bouncycastle.crypto.params.Ed25519PublicKeyParameters(pubKeymaterial, 0)); - List records = ipfs.resolveRecords(pub, 1) + List records = ipfs.resolveRecords(signer, 1) .stream() .sorted() .collect(Collectors.toList()); From 96d35a948462fbe1b7e5363a47235c7471337337 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 3 Apr 2024 17:11:01 +0100 Subject: [PATCH 184/218] Fix peer lookup reliability in Kademlia --- src/main/java/org/peergos/EmbeddedIpfs.java | 2 ++ src/main/java/org/peergos/protocol/dht/Kademlia.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 3d22a8f9..f64b2dee 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -146,6 +146,8 @@ public void start() { IdentifyBuilder.addIdentifyProtocol(node); LOG.info("Node started and listening on " + node.listenAddresses()); LOG.info("Bootstrapping IPFS routing table"); + if (bootstrap.isEmpty()) + LOG.warning("Starting with empty bootstrap list - you will not join the global dht"); int connections = dht.bootstrapRoutingTable(node, bootstrap, addr -> !addr.contains("/wss/")); LOG.info("Bootstrapping IPFS kademlia"); dht.bootstrap(node); diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 17a39e8a..4633d8fa 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -155,7 +155,7 @@ public List findClosestPeers(byte[] key, int maxCount, Host us) { Id keyId = Id.create(Hash.sha256(key), 256); SortedSet closest = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); SortedSet toQuery = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); - List localClosest = engine.getKClosestPeers(key, maxCount); + List localClosest = engine.getKClosestPeers(key, Math.max(6, maxCount)); if (maxCount == 1) { Optional match = localClosest.stream().filter(p -> Arrays.equals(p.peerId.toBytes(), key)).findFirst(); if (match.isPresent()) From e5ea311f6c915e832752e15dea7c3ae8d57749d1 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 11 Apr 2024 23:41:08 +0100 Subject: [PATCH 185/218] Add bulk get links call which can be used to efficiently mirror a whole DAG --- .../java/org/peergos/AggregatedMetrics.java | 1 + src/main/java/org/peergos/Want.java | 14 +++++++++- src/main/java/org/peergos/net/APIHandler.java | 27 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/AggregatedMetrics.java b/src/main/java/org/peergos/AggregatedMetrics.java index 3087c76f..19ae578e 100644 --- a/src/main/java/org/peergos/AggregatedMetrics.java +++ b/src/main/java/org/peergos/AggregatedMetrics.java @@ -25,6 +25,7 @@ private static Counter build(String name, String help) { public static final Counter API_BLOCK_RM = build("api_block_rm", "Total calls to block/rm."); public static final Counter API_BLOCK_RM_BULK = build("api_block_rm_bulk", "Total calls to block/rm/bulk."); public static final Counter API_BLOCK_STAT = build("api_block_stat", "Total calls to block/stat."); + public static final Counter API_BLOCK_STAT_BULK = build("api_block_stat_bulk", "Total calls to block/stat/bulk."); public static final Counter API_REFS_LOCAL = build("api_refs_local", "Total calls to refs/local."); public static final Counter API_BLOCK_HAS = build("api_block_has", "Total calls to block/has."); public static final Counter API_BLOOM_ADD = build("api_bloom_add", "Total calls to bloom/add."); diff --git a/src/main/java/org/peergos/Want.java b/src/main/java/org/peergos/Want.java index 9de2b0fd..07300294 100644 --- a/src/main/java/org/peergos/Want.java +++ b/src/main/java/org/peergos/Want.java @@ -1,10 +1,11 @@ package org.peergos; import io.ipfs.cid.Cid; +import org.peergos.config.*; import java.util.*; -public class Want { +public class Want implements Jsonable { public final Cid cid; public final Optional authHex; public Want(Cid cid, Optional authHex) { @@ -24,6 +25,17 @@ public boolean equals(Object o) { return cid.equals(want.cid) && authHex.equals(want.authHex); } + public Map toJson() { + Map m = new LinkedHashMap<>(); + m.put("c", cid.toString()); + authHex.ifPresent(h -> m.put("a", h)); + return m; + } + + public static Want fromJson(Map m) { + return new Want(Cid.decode(m.get("c")), Optional.ofNullable(m.get("a"))); + } + @Override public int hashCode() { return Objects.hash(cid, authHex); diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index a1e6af40..9de199ee 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -5,6 +5,7 @@ import io.libp2p.core.PeerId; import io.libp2p.crypto.keys.*; import org.peergos.*; +import org.peergos.cbor.*; import org.peergos.protocol.ipns.*; import org.peergos.protocol.ipns.pb.*; import org.peergos.util.*; @@ -30,6 +31,7 @@ public class APIHandler extends Handler { public static final String RM = "block/rm"; public static final String RM_BULK = "block/rm/bulk"; public static final String STAT = "block/stat"; + public static final String BULK_STAT = "block/stat/bulk"; public static final String REFS_LOCAL = "refs/local"; public static final String BLOOM_ADD = "bloom/add"; public static final String HAS = "block/has"; @@ -99,6 +101,31 @@ public void handleCallToAPI(HttpExchange httpExchange) { } break; } + case BULK_STAT: { + AggregatedMetrics.API_BLOCK_STAT_BULK.inc(); + Map json = (Map) JSONParser.parse(new String(readFully(httpExchange.getRequestBody()))); + List wants = ((List>)json.get("wants")) + .stream() + .map(Want::fromJson) + .collect(Collectors.toList()); + Set peers = Optional.ofNullable(params.get("peers")) + .map(p -> p.stream().map(PeerId::fromBase58).collect(Collectors.toSet())) + .orElse(Collections.emptySet()); + List blocks = ipfs.getBlocks(wants, peers, true); + if (wants.size() == blocks.size()) { + List> links = blocks.stream() + .map(b -> CborObject.getLinks(b.hash, b.block).stream().map(Cid::toString).collect(Collectors.toList())) + .collect(Collectors.toList()); + replyJson(httpExchange, JSONParser.toString(links)); + } else { + try { + httpExchange.sendResponseHeaders(400, 0); + } catch (IOException ioe) { + HttpUtil.replyError(httpExchange, ioe); + } + } + break; + } case PUT: { // https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-block-put AggregatedMetrics.API_BLOCK_PUT.inc(); List format = params.get("format"); From 4a433158219697f730fc0d5bfcdc8d8cf5bcf383 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 12 Apr 2024 11:22:07 +0100 Subject: [PATCH 186/218] Fix mirroring raw blocks via bulk stat api --- src/main/java/org/peergos/net/APIHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index 9de199ee..73f58352 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -114,7 +114,9 @@ public void handleCallToAPI(HttpExchange httpExchange) { List blocks = ipfs.getBlocks(wants, peers, true); if (wants.size() == blocks.size()) { List> links = blocks.stream() - .map(b -> CborObject.getLinks(b.hash, b.block).stream().map(Cid::toString).collect(Collectors.toList())) + .map(b -> b.hash.codec.equals(Cid.Codec.Raw) ? + Collections.emptyList() : + CborObject.getLinks(b.hash, b.block).stream().map(Cid::toString).collect(Collectors.toList())) .collect(Collectors.toList()); replyJson(httpExchange, JSONParser.toString(links)); } else { From de9479bf655c1a9a3d46898df5e4f705f1073656 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 12 Apr 2024 14:43:33 +0100 Subject: [PATCH 187/218] Don't return identity hash links --- src/main/java/org/peergos/net/APIHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index 73f58352..aeea8a2f 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -116,7 +116,11 @@ public void handleCallToAPI(HttpExchange httpExchange) { List> links = blocks.stream() .map(b -> b.hash.codec.equals(Cid.Codec.Raw) ? Collections.emptyList() : - CborObject.getLinks(b.hash, b.block).stream().map(Cid::toString).collect(Collectors.toList())) + CborObject.getLinks(b.hash, b.block) + .stream() + .filter(c -> c.getType() != Multihash.Type.id) + .map(Cid::toString) + .collect(Collectors.toList())) .collect(Collectors.toList()); replyJson(httpExchange, JSONParser.toString(links)); } else { From 24eba3d5c896daa77732f29489811882a585307d Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 12 Apr 2024 17:02:42 +0100 Subject: [PATCH 188/218] Add failing bitswap test --- src/test/java/org/peergos/BitswapTest.java | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/test/java/org/peergos/BitswapTest.java b/src/test/java/org/peergos/BitswapTest.java index eba5dbf6..1f6c95e5 100644 --- a/src/test/java/org/peergos/BitswapTest.java +++ b/src/test/java/org/peergos/BitswapTest.java @@ -46,6 +46,42 @@ public void getBlock() { } } + @Test + public void getTenBlocks() { + HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); + Host node1 = builder1.build(); + RamBlockstore blockstore2 = new RamBlockstore(); + HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true)); + Host node2 = builder2.build(); + node1.start().join(); + node2.start().join(); + try { + Multiaddr address2 = node2.listenAddresses().get(0); + List hashes = new ArrayList<>(); + Random random = new Random(28); + for (int i=0; i < 10; i++) { + byte[] blockData = new byte[1024*1024]; + random.nextBytes(blockData); + Cid hash = blockstore2.put(blockData, Cid.Codec.Raw).join(); + hashes.add(hash); + } + + Bitswap bitswap1 = builder1.getBitswap().get(); + node1.getAddressBook().addAddrs(address2.getPeerId(), 0, address2).join(); + List receivedBlocks = bitswap1.get(hashes.stream().map(Want::new).collect(Collectors.toList()), node1, Set.of(address2.getPeerId()), false) + .stream() + .map(f -> f.join()) + .collect(Collectors.toList()); + if (receivedBlocks.size() != hashes.size()) + throw new IllegalStateException("Incorrect number of blocks returned!"); + } finally { + node1.stop(); + node2.stop(); + } + } + @Test public void blockFlooder() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), From 3df14f4d6d6e296df53436fe7de7636d73b63662 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 17 Apr 2024 12:30:00 +0100 Subject: [PATCH 189/218] Fix receiving > 2MiB of blocks in a bitswap stream --- src/main/java/org/peergos/blockstore/RamBlockstore.java | 5 +++-- .../java/org/peergos/protocol/bitswap/BitswapEngine.java | 2 +- .../org/peergos/protocol/bitswap/BitswapProtocol.java | 9 +++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/RamBlockstore.java b/src/main/java/org/peergos/blockstore/RamBlockstore.java index 41b69e06..248bbaa2 100644 --- a/src/main/java/org/peergos/blockstore/RamBlockstore.java +++ b/src/main/java/org/peergos/blockstore/RamBlockstore.java @@ -4,6 +4,7 @@ import io.ipfs.multihash.*; import org.peergos.*; import org.peergos.blockstore.metadatadb.BlockMetadata; +import org.peergos.cbor.*; import org.peergos.util.*; import java.util.*; @@ -60,7 +61,7 @@ public CompletableFuture> refs(boolean useBlockstore) { @Override public CompletableFuture getBlockMetadata(Cid h) { - throw new IllegalStateException("Unsupported operation!"); + byte[] block = get(h).join().get(); + return Futures.of(new BlockMetadata(block.length, CborObject.getLinks(h, block))); } - } diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 381a2f60..3e9927c8 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -265,7 +265,7 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter waiter.result.complete(new HashedBlock(c, data)); localWants.remove(w); } else - LOG.info("Received block we don't want: z" + c.toBase58() + " from " + sourcePeerId.bareMultihash()); + LOG.info("Received block we don't want: " + c + " from " + sourcePeerId.bareMultihash()); } } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java index bfc9da43..4754133e 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapProtocol.java @@ -5,7 +5,7 @@ import io.prometheus.client.*; import org.jetbrains.annotations.*; import org.peergos.protocol.bitswap.pb.*; -import org.peergos.util.Logging; +import org.peergos.util.*; import java.util.concurrent.*; import java.util.logging.*; @@ -36,7 +36,7 @@ public class BitswapProtocol extends ProtobufProtocolHandler private final BitswapEngine engine; public BitswapProtocol(BitswapEngine engine) { - super(MessageOuterClass.Message.getDefaultInstance(), engine.maxMessageSize(), engine.maxMessageSize()); + super(MessageOuterClass.Message.getDefaultInstance(), engine.maxMessageSize(), Long.MAX_VALUE); this.engine = engine; } @@ -78,5 +78,10 @@ public void onMessage(@NotNull Stream stream, MessageOuterClass.Message msg) { receivedBytes.inc(msg.getSerializedSize()); engine.receiveMessage(msg, stream, sentBytes); } + + @Override + public void onException(@Nullable Throwable cause) { + LOG.log(Level.WARNING, cause.getMessage(), cause); + } } } From bcd28ffcc3e54022439c9c0db0f5bc05e98b9c80 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 17 Apr 2024 12:30:55 +0100 Subject: [PATCH 190/218] Bump version --- src/main/java/org/peergos/net/APIHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index aeea8a2f..3faca1c8 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -19,7 +19,7 @@ public class APIHandler extends Handler { public static final String API_URL = "/api/v0/"; - public static final Version CURRENT_VERSION = Version.parse("0.7.6"); + public static final Version CURRENT_VERSION = Version.parse("0.7.7"); private static final Logger LOG = Logging.LOG(); private static final boolean LOGGING = true; From 3f197e5bef4168bc8e51d44751c59d86c241e7ba Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 17 Apr 2024 13:46:01 +0100 Subject: [PATCH 191/218] Bump version in pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be66de19..2ca98887 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.6 + v0.7.7 UTF-8 From d426bc6bc627d5c31ae06d1ed8cac634e944732a Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 19 Apr 2024 09:21:41 +0100 Subject: [PATCH 192/218] Log up to 2 cids we are requesting on bitswap --- src/main/java/org/peergos/protocol/bitswap/Bitswap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java index d3448ab9..d7a02a51 100644 --- a/src/main/java/org/peergos/protocol/bitswap/Bitswap.java +++ b/src/main/java/org/peergos/protocol/bitswap/Bitswap.java @@ -124,7 +124,7 @@ public void sendWants(Host us, Set wants, Set peers) { Map haves = engine.getHaves(); // broadcast to all connected bitswap peers if none are supplied Set audience = peers.isEmpty() ? getBroadcastAudience() : peers; - LOG.info("Send wants: " + wants.size() + " to " + audience); + LOG.info("Send wants: " + wants.size() + " to " + audience + " cids: " + wants.stream().limit(2).map(w -> w.cid).collect(Collectors.toList())); List wantsProto = wants.stream() .map(want -> MessageOuterClass.Message.Wantlist.Entry.newBuilder() .setWantType(audience.size() <= 2 || haves.containsKey(want) ? From 04e5ca5f42e18d120cd634d9a2faff1a5349bc44 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 19 Apr 2024 09:28:07 +0100 Subject: [PATCH 193/218] Do not try and dial DNS multiaddr in Kademlia which is a privacy leak --- src/main/java/org/peergos/protocol/dht/Kademlia.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 4633d8fa..07cccbb7 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -293,6 +293,7 @@ private Multiaddr[] getPublic(PeerAddresses target) { private CompletableFuture dialPeer(PeerAddresses target, Host us) { Multiaddr[] multiaddrs = target.addresses.stream() .map(a -> Multiaddr.fromString(a.toString())) + .filter(a -> ! a.has(Protocol.DNS) && ! a.has(Protocol.DNS4) && ! a.has(Protocol.DNS6)) .collect(Collectors.toList()).toArray(new Multiaddr[0]); return dial(us, PeerId.fromBase58(target.peerId.toBase58()), multiaddrs).getController(); } From 3f7eb42e869e42335c750faa31ac0e697b43e92e Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 29 Apr 2024 10:30:47 +0100 Subject: [PATCH 194/218] Remove bouncy castle signatures from jar --- pom.xml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pom.xml b/pom.xml index 2ca98887..54802f42 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,25 @@ ${kotlin.version} true + + org.apache.maven.plugins + maven-dependency-plugin + 2.6 + + + unpack-dependencies + package + + unpack-dependencies + + + system + META-INF/*.SF,META-INF/*.DSA,META-INF/*.RSA + ${project.build.directory}/classes + + + + org.apache.maven.plugins maven-compiler-plugin @@ -101,6 +120,16 @@ true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + From 1da5bbf13758bd4c4660642276be8c4a0a26782c Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 29 Apr 2024 10:31:58 +0100 Subject: [PATCH 195/218] Bump version v0.7.8 --- pom.xml | 2 +- src/main/java/org/peergos/net/APIHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 54802f42..4737fcaf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.7 + v0.7.8 UTF-8 diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index 3faca1c8..caefc788 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -19,7 +19,7 @@ public class APIHandler extends Handler { public static final String API_URL = "/api/v0/"; - public static final Version CURRENT_VERSION = Version.parse("0.7.7"); + public static final Version CURRENT_VERSION = Version.parse("0.7.8"); private static final Logger LOG = Logging.LOG(); private static final boolean LOGGING = true; From 12aef003b20548f305bffa55cfdaf31f5ecc710e Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 2 May 2024 11:48:23 +0100 Subject: [PATCH 196/218] Implement MDNS based discovery for local networks Remove Perf.kt --- src/main/java/org/peergos/EmbeddedIpfs.java | 14 ++ .../java/org/peergos/client/PerfTest.java | 116 ------------- .../peergos/protocol/perf/Correlation.java | 12 -- .../java/org/peergos/protocol/perf/Perf.kt | 156 ------------------ .../java/org/peergos/EmbeddedIpfsTest.java | 19 +++ 5 files changed, 33 insertions(+), 284 deletions(-) delete mode 100644 src/main/java/org/peergos/client/PerfTest.java delete mode 100755 src/main/java/org/peergos/protocol/perf/Correlation.java delete mode 100644 src/main/java/org/peergos/protocol/perf/Perf.kt diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index f64b2dee..cd70fc4b 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -7,6 +7,7 @@ import io.libp2p.core.crypto.*; import io.libp2p.core.multiformats.*; import io.libp2p.core.multistream.*; +import io.libp2p.discovery.*; import io.libp2p.protocol.*; import org.peergos.blockstore.*; import org.peergos.blockstore.metadatadb.BlockMetadataStore; @@ -26,6 +27,7 @@ import org.peergos.protocol.ipns.*; import org.peergos.util.Logging; +import java.net.*; import java.nio.file.*; import java.sql.Connection; import java.sql.DriverManager; @@ -152,6 +154,18 @@ public void start() { LOG.info("Bootstrapping IPFS kademlia"); dht.bootstrap(node); dht.startBootstrapThread(node); + InetAddress address = null; + MDnsDiscovery mdns = new MDnsDiscovery(node, "_ipfs-discovery._udp.local.", 60, address); + mdns.getNewPeerFoundListeners().add(peerInfo -> { + PeerId remote = PeerId.fromBase58(peerInfo.getPeerId().toBase58().substring(1)); // Not what's wrong with peerInfo, but this works + if (! remote.equals(node.getPeerId())) { + LOG.info(node.getPeerId() + " found local peer: " + peerInfo.getPeerId().toBase58() + ", addrs: " + peerInfo.getAddresses()); + KademliaController ctr = dht.dial(node, remote, peerInfo.getAddresses().toArray(new Multiaddr[0])).getController().join(); + ctr.closerPeers(node.getPeerId().getBytes()).join(); + } + return null; + }); + mdns.start(); blockProvider.ifPresent(p -> p.start()); } diff --git a/src/main/java/org/peergos/client/PerfTest.java b/src/main/java/org/peergos/client/PerfTest.java deleted file mode 100644 index 2dbe8195..00000000 --- a/src/main/java/org/peergos/client/PerfTest.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.peergos.client; - -import io.libp2p.core.Host; -import io.libp2p.core.PeerId; -import io.libp2p.core.Stream; -import io.libp2p.core.StreamPromise; -import io.libp2p.core.crypto.PrivKey; -import io.libp2p.core.dsl.Builder; -import io.libp2p.core.dsl.BuilderJKt; -import io.libp2p.core.multiformats.Multiaddr; -import io.libp2p.core.mux.StreamMuxerProtocol; -import io.libp2p.crypto.keys.Ed25519Kt; -import io.libp2p.security.noise.NoiseXXSecureChannel; -import io.libp2p.transport.tcp.TcpTransport; -import org.peergos.HostBuilder; -import org.peergos.protocol.perf.Perf; -import org.peergos.protocol.perf.PerfController; -import org.peergos.util.Pair; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -public class PerfTest { - - private Pair> buildHost(boolean isServer) { - String base64PrivKeyStr = "CAESIPtYePRNDRG9RrQ06shBER+MVUyEFJHtrFMiNgabSj1j"; - byte[] privKeyBytes = io.ipfs.multibase.binary.Base64.decodeBase64(base64PrivKeyStr); - org.peergos.HostBuilder builder = new org.peergos.HostBuilder().setIdentity(privKeyBytes); - Optional serverPeerId = Optional.empty(); - if (!isServer) { - serverPeerId = Optional.of(builder.getPeerId()); - PrivKey privKey = Ed25519Kt.generateEd25519KeyPair().getFirst(); - builder = new org.peergos.HostBuilder().setIdentity(privKey.bytes()); - } - return new Pair<>(builder, serverPeerId); - } - - private PerfTest(boolean isServer, String otherServerAddress, String transport, - int uploadBytes, int downloadBytes) throws Exception { - if (!transport.equals("tcp")) { - throw new IllegalArgumentException("Invalid transport protocol: " + transport); - } - int port = 10000 + new Random().nextInt(50000); - String[] serverAddressParts = !isServer ? otherServerAddress.split(":") : - new String[]{"127.0.0.1", "" + port}; - if (!isServer && serverAddressParts.length != 2) { - throw new IllegalArgumentException("Invalid serverAddress! Expecting - :"); - } - String listenAddress = "/ip4/" + serverAddressParts[0] + "/tcp/" + serverAddressParts[1]; - - Pair> hostPair = buildHost(isServer); - HostBuilder serverBuilder = hostPair.left; - PrivKey privKey = serverBuilder.getPrivateKey(); - Host node = BuilderJKt.hostJ(Builder.Defaults.None, b -> { - b.getIdentity().setFactory(() -> privKey); - b.getTransports().add(TcpTransport::new); - b.getSecureChannels().add((k, m) -> new NoiseXXSecureChannel(k, m)); - - List muxers = new ArrayList<>(); - muxers.add(StreamMuxerProtocol.getYamux()); - b.getMuxers().addAll(muxers); - if (isServer) { - b.getProtocols().add(new Perf(uploadBytes, downloadBytes)); - - Multiaddr serverMultiAddr = Multiaddr.fromString(listenAddress); - b.getNetwork().listen(serverMultiAddr.toString()); - b.getConnectionHandlers().add(conn -> System.err.println(conn.localAddress() + - " received connection from " + conn.remoteAddress() + - " on transport " + conn.transport())); - } - }); - node.start().join(); - if (isServer) { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - System.err.println("Shutting down server"); - node.stop(); - })); - } - if (isServer) { - System.err.println("Server started. Address: " + listenAddress + " waiting for connection..."); - } else { - PeerId otherHostPeerId = hostPair.right.get(); - StreamPromise perf = node - .getNetwork() - .connect(otherHostPeerId, new Multiaddr(listenAddress)) - .thenApply(it -> it.muxerSession().createStream(new Perf(uploadBytes, downloadBytes))) - .join(); - - Stream perfStream = perf.getStream().get(5, TimeUnit.SECONDS); - PerfController perfCtr = perf.getController().get(5, TimeUnit.SECONDS); - - double latency = perfCtr.perf().join() / 1000.0; - perfStream.close().get(5, TimeUnit.SECONDS); - node.stop(); - System.out.println("{\"type\": \"final\",\"timeSeconds\": " + latency + ",\"uploadBytes\": " + uploadBytes - + ",\"downloadBytes\": " + downloadBytes + "}"); - } - } - public static void main(String[] args) { - try { - boolean isServer = true;//Boolean.parseBoolean(System.getenv("run-server")); - String serverAddress = null;//System.getenv("server-address"); - String transport = "tcp";//System.getenv("transport"); - int uploadBytes = 10 * 1024 * 1024; //Integer.parseInt("1");//System.getenv("upload-bytes"); - int downloadBytes = 5 * 1024 * 1024; //Integer.parseInt("1");;//System.getenv("download-bytes"); - new PerfTest(isServer, serverAddress, transport, uploadBytes, downloadBytes); - } catch (Throwable t) { - t.printStackTrace(); - System.err.println("Unexpected exit: " + t); - System.exit(-1); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/peergos/protocol/perf/Correlation.java b/src/main/java/org/peergos/protocol/perf/Correlation.java deleted file mode 100755 index ad823dbf..00000000 --- a/src/main/java/org/peergos/protocol/perf/Correlation.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.peergos.protocol.perf; - -public class Correlation { - - public static int id(byte[] input) { - if(input.length < 4) { - throw new IllegalArgumentException("Input length must be >=4"); - } - return (input[0] & 0xFF) | ((input[1] & 0xFF) << 8) | ((input[2] & 0xFF) << 16) | ((input[3] & 0xFF) << 24); - } - -} diff --git a/src/main/java/org/peergos/protocol/perf/Perf.kt b/src/main/java/org/peergos/protocol/perf/Perf.kt deleted file mode 100644 index 951971ce..00000000 --- a/src/main/java/org/peergos/protocol/perf/Perf.kt +++ /dev/null @@ -1,156 +0,0 @@ -package org.peergos.protocol.perf - -import io.libp2p.core.BadPeerException -import io.libp2p.core.ConnectionClosedException -import io.libp2p.core.Libp2pException -import io.libp2p.core.Stream -import io.libp2p.core.multistream.StrictProtocolBinding -import io.libp2p.etc.types.completedExceptionally -import io.libp2p.etc.types.lazyVar -import io.libp2p.etc.types.toByteArray -import io.libp2p.protocol.ProtocolHandler -import io.libp2p.protocol.ProtocolMessageHandler -import io.netty.buffer.ByteBuf -import io.netty.channel.ChannelHandlerContext -import io.netty.handler.codec.ByteToMessageCodec -import java.time.Duration -import java.util.* -import java.util.concurrent.CompletableFuture -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - - -/* -Attempt to implement: https://github.com/libp2p/test-plans/tree/master/perf#libp2p-performance-benchmarking - -still to add the following every second... - double latency = (currentMillisecond - startMillisecond) / 1000.0; - System.out.println("{\"type\": \"intermediary\",\"timeSeconds\": " + latency + - ",\"uploadBytes\": " + totalUploadSize - + ",\"downloadBytes\": " + total + "}"); - */ -interface PerfController { - fun perf(): CompletableFuture -} - -class Perf(uploadSize: Int, downloadSize: Int) : PerfBinding(PerfProtocol(uploadSize, downloadSize)) - -open class PerfBinding(perf: PerfProtocol) : - StrictProtocolBinding("/perf/0.1.0", perf) - -class PerfTimeoutException : Libp2pException() - -open class PerfProtocol(var uploadSize: Int, var downloadSize: Int) : ProtocolHandler(Long.MAX_VALUE, Long.MAX_VALUE) { - var timeoutScheduler by lazyVar { Executors.newSingleThreadScheduledExecutor() } - var curTime: () -> Long = { System.currentTimeMillis() } - var random = Random() - var perfTimeout = Duration.ofHours(10) //kev - var totalUploadSize = uploadSize + 4 - var totalDownloadSize = downloadSize + 4 - override fun onStartInitiator(stream: Stream): CompletableFuture { - val handler = PerfInitiator() - stream.pushHandler(PerfCodec()) - stream.pushHandler(handler) - stream.pushHandler(PerfCodec()) - return handler.activeFuture - } - - override fun onStartResponder(stream: Stream): CompletableFuture { - val handler = PerfResponder() - stream.pushHandler(PerfCodec()) - stream.pushHandler(handler) - stream.pushHandler(PerfCodec()) - return CompletableFuture.completedFuture(handler) - } - - open class PerfCodec : ByteToMessageCodec() { - override fun encode(ctx: ChannelHandlerContext?, msg: ByteArray, out: ByteBuf) { - out.writeInt(msg.size) - out.writeBytes(msg) - } - - override fun decode(ctx: ChannelHandlerContext?, msg: ByteBuf, out: MutableList) { - val readerIndex = msg.readerIndex() - if (msg.readableBytes() < 4) { - return - } - val len = msg.readInt() - val readable = msg.readableBytes() - if (readable < len) { - // not enough data to read the full array - // will wait for more ... - msg.readerIndex(readerIndex) - return - } - val data = msg.readSlice(len) - out.add(data.toByteArray()) - } - } - - open inner class PerfResponder : ProtocolMessageHandler, PerfController { - override fun onMessage(stream: Stream, msg: ByteArray) { - val arr = ByteArray(totalDownloadSize) - random.nextBytes(arr) - //copy across correlation id - for (i in 0..3) { - arr[i] = msg[i] - } - stream.writeAndFlush(arr) - } - - override fun perf(): CompletableFuture { - throw Libp2pException("This is perf responder only") - } - } - - open inner class PerfInitiator : ProtocolMessageHandler, PerfController { - val activeFuture = CompletableFuture() - val requests = Collections.synchronizedMap(mutableMapOf>>()) - lateinit var stream: Stream - var closed = false - - override fun onActivated(stream: Stream) { - this.stream = stream - activeFuture.complete(this) - } - - override fun onMessage(stream: Stream, msg: ByteArray) { - val dataS = "" + Correlation.id(msg) - val (sentT, future) = requests.remove(dataS) - ?: throw BadPeerException("Unknown or expired perf data in response: $dataS") - future.complete((curTime() - sentT)) - } - - override fun onClosed(stream: Stream) { - synchronized(requests) { - closed = true - requests.values.forEach { it.second.completeExceptionally(ConnectionClosedException()) } - requests.clear() - timeoutScheduler.shutdownNow() - } - activeFuture.completeExceptionally(ConnectionClosedException()) - } - - override fun perf(): CompletableFuture { - val ret = CompletableFuture() - val arr = ByteArray(totalUploadSize) - random.nextBytes(arr) - val start = curTime() - val dataS = "" + Correlation.id(arr) - synchronized(requests) { - if (closed) return completedExceptionally(ConnectionClosedException()) - requests[dataS] = start to ret - - timeoutScheduler.schedule( - { - requests.remove(dataS)?.second?.completeExceptionally(PerfTimeoutException()) - }, - perfTimeout.toMillis(), - TimeUnit.MILLISECONDS - ) - } - stream.writeAndFlush(arr) - return ret - } - } -} \ No newline at end of file diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index d1457f6b..dc15bfc4 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -43,6 +43,25 @@ public void largeBlock() throws Exception { node2.stop(); } + @Test + public void mdnsDiscovery() throws Exception { + EmbeddedIpfs node1 = build(Collections.emptyList(), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); + node1.start(); + EmbeddedIpfs node2 = build(Collections.emptyList(), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); + node2.start(); + + Thread.sleep(5_000); + Cid block = node2.blockstore.put(new byte[1024], Cid.Codec.Raw).join(); + PeerId peerId2 = node2.node.getPeerId(); + List retrieved = ForkJoinPool.commonPool().submit( + () -> node1.getBlocks(List.of(new Want(block)), Set.of(peerId2), false)) + .get(5, TimeUnit.SECONDS); + Assert.assertTrue(retrieved.size() == 1); + + node1.stop(); + node2.stop(); + } + @Test public void publishValue() throws Exception { EmbeddedIpfs node1 = build(BootstrapTest.BOOTSTRAP_NODES, List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); From fb1272977d6ec33feebdb8f774750dc9829078b0 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 2 May 2024 11:51:12 +0100 Subject: [PATCH 197/218] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ef6349c..fadf89d1 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Currently implemented properties: * bloom/[infini filtered](https://www.rasmuspagh.net/papers/infinifilter.pdf) blockstore * connect bitswap to kademlia for discovery, with a faster version with supplied peerids * configurable cid publishing function +* mDNS peer discovery * Android compatibility * example serverless chat app using p2p http proxy for Android * interop tests with other implementations - https://github.com/libp2p/test-plans/ @@ -37,7 +38,6 @@ In the future we will add: * circuit-relay * dcutr (direct connection upgrade through relay) * AutoRelay -* mDNS peer discovery * example iOS chat app * QUIC transport (and encryption and multiplexing) From 21d438432a0a5e67d100668cb35f2eec3b24b335 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 2 May 2024 15:25:52 +0100 Subject: [PATCH 198/218] Make sure MDNS uses all network interfaces Also make sure mdns is stopped with the node --- src/main/java/org/peergos/EmbeddedIpfs.java | 39 +++++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index cd70fc4b..bfadd72d 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -27,6 +27,7 @@ import org.peergos.protocol.ipns.*; import org.peergos.util.Logging; +import java.io.*; import java.net.*; import java.nio.file.*; import java.sql.Connection; @@ -51,6 +52,7 @@ public class EmbeddedIpfs { public final Optional p2pHttp; private final List bootstrap; private final Optional blockProvider; + private final List mdns = new ArrayList<>(); public EmbeddedIpfs(Host node, Blockstore blockstore, @@ -154,18 +156,30 @@ public void start() { LOG.info("Bootstrapping IPFS kademlia"); dht.bootstrap(node); dht.startBootstrapThread(node); - InetAddress address = null; - MDnsDiscovery mdns = new MDnsDiscovery(node, "_ipfs-discovery._udp.local.", 60, address); - mdns.getNewPeerFoundListeners().add(peerInfo -> { - PeerId remote = PeerId.fromBase58(peerInfo.getPeerId().toBase58().substring(1)); // Not what's wrong with peerInfo, but this works - if (! remote.equals(node.getPeerId())) { - LOG.info(node.getPeerId() + " found local peer: " + peerInfo.getPeerId().toBase58() + ", addrs: " + peerInfo.getAddresses()); - KademliaController ctr = dht.dial(node, remote, peerInfo.getAddresses().toArray(new Multiaddr[0])).getController().join(); - ctr.closerPeers(node.getPeerId().getBytes()).join(); + + try { + List ourAddrs = Collections.list(NetworkInterface.getNetworkInterfaces()).stream() + .flatMap(net -> net.getInterfaceAddresses().stream() + .map(InterfaceAddress::getAddress)) + .collect(Collectors.toList()); + for (InetAddress addr: ourAddrs) { + LOG.info("MDNS using " + addr); + MDnsDiscovery mdns = new MDnsDiscovery(node, "_ipfs-discovery._udp.local.", 60, addr); + this.mdns.add(mdns); + mdns.getNewPeerFoundListeners().add(peerInfo -> { + PeerId remote = PeerId.fromBase58(peerInfo.getPeerId().toBase58().substring(1)); // Not what's wrong with peerInfo, but this works + if (!remote.equals(node.getPeerId())) { + LOG.info(node.getPeerId() + " found local peer: " + peerInfo.getPeerId().toBase58() + ", addrs: " + peerInfo.getAddresses()); + KademliaController ctr = dht.dial(node, remote, peerInfo.getAddresses().toArray(new Multiaddr[0])).getController().join(); + ctr.closerPeers(node.getPeerId().getBytes()).join(); + } + return null; + }); + mdns.start(); } - return null; - }); - mdns.start(); + } catch (IOException e) { + LOG.log(Level.INFO, e.getMessage(), e); + } blockProvider.ifPresent(p -> p.start()); } @@ -176,6 +190,9 @@ public CompletableFuture stop() throws Exception { } blockProvider.ifPresent(b -> b.stop()); dht.stopBootstrapThread(); + for (MDnsDiscovery m : mdns) { + m.stop(); + } return node != null ? node.stop() : CompletableFuture.completedFuture(null); } From 138569e6f96468d6e0d16b1e318ce276b4910708 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 3 May 2024 21:01:11 +0100 Subject: [PATCH 199/218] gh actions v4 --- .github/workflows/maven.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 96c7d573..2b6a211b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/actions/local-s3 - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: temurin java-version: 11 From 1fa1d456f74e1088db9a8da13908405787acd19f Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 8 May 2024 16:05:07 +0100 Subject: [PATCH 200/218] Log no local peers in kademlia table --- src/main/java/org/peergos/protocol/dht/Kademlia.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 07cccbb7..6a39c0c2 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -165,6 +165,8 @@ public List findClosestPeers(byte[] key, int maxCount, Host us) { .map(p -> new RoutingEntry(Id.create(Hash.sha256(p.peerId.toBytes()), 256), p)) .collect(Collectors.toList())); toQuery.addAll(closest); + if (toQuery.isEmpty()) + LOG.info("Couldn't find any local peers in kademlia routing table"); Set queried = Collections.synchronizedSet(new HashSet<>()); int queryParallelism = 3; while (true) { From cf4eacaa2d594b0a4952a319aa5acc3e6702f95a Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 8 May 2024 22:07:10 +0100 Subject: [PATCH 201/218] v0.7.9 Remove shade plugin, kotlin and recently added dependency plugin --- pom.xml | 53 +---------------------------------------------------- 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/pom.xml b/pom.xml index 4737fcaf..708dc041 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.8 + v0.7.9 UTF-8 @@ -41,31 +41,6 @@ - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - true - - - org.apache.maven.plugins - maven-dependency-plugin - 2.6 - - - unpack-dependencies - package - - unpack-dependencies - - - system - META-INF/*.SF,META-INF/*.DSA,META-INF/*.RSA - ${project.build.directory}/classes - - - - org.apache.maven.plugins maven-compiler-plugin @@ -108,32 +83,6 @@ - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 - - - package - - shade - - - true - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - From e47a8d59b2ba290465466817338a2841ba7b589c Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 15 May 2024 16:05:08 +0100 Subject: [PATCH 202/218] Make sure we add local addresses of nodes discovered via mdns --- src/main/java/org/peergos/EmbeddedIpfs.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index bfadd72d..60251eb5 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -170,8 +170,10 @@ public void start() { PeerId remote = PeerId.fromBase58(peerInfo.getPeerId().toBase58().substring(1)); // Not what's wrong with peerInfo, but this works if (!remote.equals(node.getPeerId())) { LOG.info(node.getPeerId() + " found local peer: " + peerInfo.getPeerId().toBase58() + ", addrs: " + peerInfo.getAddresses()); - KademliaController ctr = dht.dial(node, remote, peerInfo.getAddresses().toArray(new Multiaddr[0])).getController().join(); + Multiaddr[] remoteAddrs = peerInfo.getAddresses().toArray(new Multiaddr[0]); + KademliaController ctr = dht.dial(node, remote, remoteAddrs).getController().join(); ctr.closerPeers(node.getPeerId().getBytes()).join(); + node.getAddressBook().addAddrs(remote, 0, remoteAddrs); } return null; }); From 0bc8bbdbac26375686618a2f6a95b6f20b015825 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 20 May 2024 22:37:10 +0100 Subject: [PATCH 203/218] Fix mdns with ipv6 wildcard listen address --- pom.xml | 2 +- src/main/java/org/peergos/EmbeddedIpfs.java | 38 ++++++++------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index 708dc041..0bb1a67c 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ com.github.peergos jvm-libp2p - 0.16.6 + 0.18.0-mdns org.bouncycastle diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 60251eb5..8ce24a6a 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -157,31 +157,21 @@ public void start() { dht.bootstrap(node); dht.startBootstrapThread(node); - try { - List ourAddrs = Collections.list(NetworkInterface.getNetworkInterfaces()).stream() - .flatMap(net -> net.getInterfaceAddresses().stream() - .map(InterfaceAddress::getAddress)) - .collect(Collectors.toList()); - for (InetAddress addr: ourAddrs) { - LOG.info("MDNS using " + addr); - MDnsDiscovery mdns = new MDnsDiscovery(node, "_ipfs-discovery._udp.local.", 60, addr); - this.mdns.add(mdns); - mdns.getNewPeerFoundListeners().add(peerInfo -> { - PeerId remote = PeerId.fromBase58(peerInfo.getPeerId().toBase58().substring(1)); // Not what's wrong with peerInfo, but this works - if (!remote.equals(node.getPeerId())) { - LOG.info(node.getPeerId() + " found local peer: " + peerInfo.getPeerId().toBase58() + ", addrs: " + peerInfo.getAddresses()); - Multiaddr[] remoteAddrs = peerInfo.getAddresses().toArray(new Multiaddr[0]); - KademliaController ctr = dht.dial(node, remote, remoteAddrs).getController().join(); - ctr.closerPeers(node.getPeerId().getBytes()).join(); - node.getAddressBook().addAddrs(remote, 0, remoteAddrs); - } - return null; - }); - mdns.start(); + LOG.info("MDNS discovery enabled"); + MDnsDiscovery mdns = new MDnsDiscovery(node, "_ipfs-discovery._udp.local.", 60, null); + this.mdns.add(mdns); + mdns.getNewPeerFoundListeners().add(peerInfo -> { + PeerId remote = PeerId.fromBase58(peerInfo.getPeerId().toBase58().substring(1)); // Not sure what's wrong with peerInfo, but this works + if (!remote.equals(node.getPeerId())) { + LOG.info(node.getPeerId() + " found local peer: " + peerInfo.getPeerId().toBase58() + ", addrs: " + peerInfo.getAddresses()); + Multiaddr[] remoteAddrs = peerInfo.getAddresses().toArray(new Multiaddr[0]); + KademliaController ctr = dht.dial(node, remote, remoteAddrs).getController().join(); + ctr.closerPeers(node.getPeerId().getBytes()).join(); + node.getAddressBook().addAddrs(remote, 0, remoteAddrs); } - } catch (IOException e) { - LOG.log(Level.INFO, e.getMessage(), e); - } + return null; + }); + mdns.start(); blockProvider.ifPresent(p -> p.start()); } From 7cce49cb42cb0de1f9e6e8f41936dd33d1117143 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 22 May 2024 14:12:32 +0100 Subject: [PATCH 204/218] Fix mdns with ipv6 wildcard listen address --- pom.xml | 2 +- src/main/java/org/peergos/EmbeddedIpfs.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 0bb1a67c..1acd2f91 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ com.github.peergos jvm-libp2p - 0.18.0-mdns + 0.18.0-ipv6-mdns-wildcard org.bouncycastle diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 8ce24a6a..8eebb3bf 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -160,10 +160,10 @@ public void start() { LOG.info("MDNS discovery enabled"); MDnsDiscovery mdns = new MDnsDiscovery(node, "_ipfs-discovery._udp.local.", 60, null); this.mdns.add(mdns); - mdns.getNewPeerFoundListeners().add(peerInfo -> { + mdns.addHandler(peerInfo -> { PeerId remote = PeerId.fromBase58(peerInfo.getPeerId().toBase58().substring(1)); // Not sure what's wrong with peerInfo, but this works if (!remote.equals(node.getPeerId())) { - LOG.info(node.getPeerId() + " found local peer: " + peerInfo.getPeerId().toBase58() + ", addrs: " + peerInfo.getAddresses()); + LOG.info(node.getPeerId() + " found local peer: " + remote.toBase58() + ", addrs: " + peerInfo.getAddresses()); Multiaddr[] remoteAddrs = peerInfo.getAddresses().toArray(new Multiaddr[0]); KademliaController ctr = dht.dial(node, remote, remoteAddrs).getController().join(); ctr.closerPeers(node.getPeerId().getBytes()).join(); From 1e8e65db29fb533dc5701a6ceed040a5009839e5 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 16 Sep 2024 13:31:14 +0100 Subject: [PATCH 205/218] Allow adding explicit announce addresses (e.g. an external NAT address) --- src/main/java/org/peergos/EmbeddedIpfs.java | 17 ++++++++++++----- .../org/peergos/protocol/IdentifyBuilder.java | 4 ++-- src/test/java/org/peergos/APIServiceTest.java | 4 ++-- .../java/org/peergos/BitswapMirrorTest.java | 2 +- src/test/java/org/peergos/BootstrapTest.java | 2 +- src/test/java/org/peergos/FindPeerTest.java | 2 +- src/test/java/org/peergos/HandlerTest.java | 6 +++--- src/test/java/org/peergos/HttpProxyTest.java | 4 +--- src/test/java/org/peergos/IpnsTest.java | 4 ++-- src/test/java/org/peergos/KademliaTest.java | 8 ++++---- .../java/org/peergos/KuboFindProviderTest.java | 2 +- src/test/java/org/peergos/KuboPingTest.java | 2 +- src/test/java/org/peergos/KuboTest.java | 2 +- src/test/java/org/peergos/PingTest.java | 8 ++++---- 14 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 8eebb3bf..321127b7 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -52,6 +52,7 @@ public class EmbeddedIpfs { public final Optional p2pHttp; private final List bootstrap; private final Optional blockProvider; + private final List announce; private final List mdns = new ArrayList<>(); public EmbeddedIpfs(Host node, @@ -61,7 +62,8 @@ public EmbeddedIpfs(Host node, Bitswap bitswap, Optional p2pHttp, List bootstrap, - Optional> newBlockProvider) { + Optional> newBlockProvider, + List announce) { this.node = node; this.blockstore = blockstore; this.records = records; @@ -72,6 +74,7 @@ public EmbeddedIpfs(Host node, this.blocks = new BitswapBlockService(node, bitswap, dht); this.blockProvider = newBlockProvider.map(q -> new PeriodicBlockProvider(22 * 3600_000L, () -> blockstore.refs(false).join().stream(), node, dht, q)); + this.announce = announce; } public int maxBlockSize() { @@ -147,7 +150,10 @@ public void start() { }); Runtime.getRuntime().addShutdownHook(shutdownHook); node.start().join(); - IdentifyBuilder.addIdentifyProtocol(node); + IdentifyBuilder.addIdentifyProtocol(node, announce.stream() + .map(MultiAddress::toString) + .map(Multiaddr::new) + .collect(Collectors.toList())); LOG.info("Node started and listening on " + node.listenAddresses()); LOG.info("Bootstrapping IPFS routing table"); if (bootstrap.isEmpty()) @@ -246,8 +252,8 @@ public static EmbeddedIpfs build(RecordStore records, IdentitySection identity, BlockRequestAuthoriser authoriser, Optional handler) { - return build(records, blocks, provideBlocks, swarmAddresses, bootstrap, identity, authoriser, handler, - Optional.empty(), Optional.empty()); + return build(records, blocks, provideBlocks, swarmAddresses, bootstrap, identity, Collections.emptyList(), + authoriser, handler, Optional.empty(), Optional.empty()); } public static EmbeddedIpfs build(RecordStore records, @@ -256,6 +262,7 @@ public static EmbeddedIpfs build(RecordStore records, List swarmAddresses, List bootstrap, IdentitySection identity, + List announce, BlockRequestAuthoriser authoriser, Optional handler, Optional bitswapProtocolId, @@ -291,7 +298,7 @@ public static EmbeddedIpfs build(RecordStore records, Optional> newBlockProvider = provideBlocks ? Optional.of(((ProvidingBlockstore)blockstore).toPublish) : Optional.empty(); - return new EmbeddedIpfs(node, blockstore, records, dht, bitswap, httpHandler, bootstrap, newBlockProvider); + return new EmbeddedIpfs(node, blockstore, records, dht, bitswap, httpHandler, bootstrap, newBlockProvider, announce); } public static Multiaddr[] getAddresses(Host node, Kademlia dht, Multihash targetNodeId) throws ConnectionException { diff --git a/src/main/java/org/peergos/protocol/IdentifyBuilder.java b/src/main/java/org/peergos/protocol/IdentifyBuilder.java index 402120c6..78a887f9 100644 --- a/src/main/java/org/peergos/protocol/IdentifyBuilder.java +++ b/src/main/java/org/peergos/protocol/IdentifyBuilder.java @@ -15,12 +15,12 @@ public class IdentifyBuilder { - public static void addIdentifyProtocol(Host node) { + public static void addIdentifyProtocol(Host node, List announceAddresses) { IdentifyOuterClass.Identify.Builder identifyBuilder = IdentifyOuterClass.Identify.newBuilder() .setProtocolVersion("ipfs/0.1.0") .setAgentVersion("nabu/v0.1.0") .setPublicKey(ByteArrayExtKt.toProtobuf(node.getPrivKey().publicKey().bytes())) - .addAllListenAddrs(node.listenAddresses().stream() + .addAllListenAddrs(Stream.concat(node.listenAddresses().stream(), announceAddresses.stream()) .flatMap(a -> expandWildcardAddresses(a).stream()) .map(Multiaddr::serialize) .map(ByteArrayExtKt::toProtobuf) diff --git a/src/test/java/org/peergos/APIServiceTest.java b/src/test/java/org/peergos/APIServiceTest.java index 9ecd3db8..8f1763ca 100644 --- a/src/test/java/org/peergos/APIServiceTest.java +++ b/src/test/java/org/peergos/APIServiceTest.java @@ -45,7 +45,7 @@ public void runAPIServiceWithFileStorageTest() { @Test public void bulkGetTest() { EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, - null, null, Optional.empty(), Collections.emptyList(), Optional.empty()); + null, null, Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); Cid cid1 = ipfs.blockstore.put("Hello".getBytes(), Cid.Codec.Raw).join(); Cid cid2= ipfs.blockstore.put("world!".getBytes(), Cid.Codec.Raw).join(); List wants = new ArrayList<>(); @@ -57,7 +57,7 @@ public void bulkGetTest() { public static void runAPIServiceTest(Blockstore blocks) { EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, - null, null, Optional.empty(), Collections.emptyList(), Optional.empty()); + null, null, Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); Cid cid = Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"); Assert.assertFalse("cid found", ipfs.blockstore.has(cid).join()); String text = "Hello world!"; diff --git a/src/test/java/org/peergos/BitswapMirrorTest.java b/src/test/java/org/peergos/BitswapMirrorTest.java index 7ba9fd36..04d1762f 100644 --- a/src/test/java/org/peergos/BitswapMirrorTest.java +++ b/src/test/java/org/peergos/BitswapMirrorTest.java @@ -26,7 +26,7 @@ public void mirrorTree() throws IOException { new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); IPFS kubo = new IPFS("localhost", 5001); Multiaddr kuboAddress = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); preloadBlocksToKubo(kubo); diff --git a/src/test/java/org/peergos/BootstrapTest.java b/src/test/java/org/peergos/BootstrapTest.java index 912356b7..770252e6 100644 --- a/src/test/java/org/peergos/BootstrapTest.java +++ b/src/test/java/org/peergos/BootstrapTest.java @@ -40,7 +40,7 @@ public void bootstrap() { new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); Multihash node1Id = Multihash.deserialize(node1.getPeerId().getBytes()); try { diff --git a/src/test/java/org/peergos/FindPeerTest.java b/src/test/java/org/peergos/FindPeerTest.java index 18d71d57..4f013878 100644 --- a/src/test/java/org/peergos/FindPeerTest.java +++ b/src/test/java/org/peergos/FindPeerTest.java @@ -21,7 +21,7 @@ public void findLongRunningNode() { new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { // bootstrap node 1 diff --git a/src/test/java/org/peergos/HandlerTest.java b/src/test/java/org/peergos/HandlerTest.java index 3aed6452..8ad69ba6 100644 --- a/src/test/java/org/peergos/HandlerTest.java +++ b/src/test/java/org/peergos/HandlerTest.java @@ -46,7 +46,7 @@ public void codecTest() { apiServer = HttpServer.create(localAPIAddress, 500); Blockstore blocks = new TypeLimitedBlockstore(new RamBlockstore(), Set.of(Cid.Codec.Raw)); EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, null, - new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty()); + new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); @@ -100,7 +100,7 @@ public void findBlockProviderTest() throws IOException { apiServer = HttpServer.create(localAPIAddress, 500); EmbeddedIpfs ipfs = new EmbeddedIpfs(node1, new ProvidingBlockstore(new RamBlockstore()), null, dht, - null, Optional.empty(), Collections.emptyList(), Optional.empty()); + null, Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); @@ -126,7 +126,7 @@ public void blockMethodsTest() { apiServer = HttpServer.create(localAPIAddress, 500); EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, - null, new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty()); + null, new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); diff --git a/src/test/java/org/peergos/HttpProxyTest.java b/src/test/java/org/peergos/HttpProxyTest.java index 046d0d4d..0e3fc26a 100644 --- a/src/test/java/org/peergos/HttpProxyTest.java +++ b/src/test/java/org/peergos/HttpProxyTest.java @@ -12,11 +12,9 @@ import org.peergos.protocol.dht.*; import org.peergos.protocol.http.*; import org.peergos.util.*; -import org.peergos.util.HttpUtil; import java.io.*; import java.net.*; -import java.nio.charset.*; import java.util.*; import java.util.concurrent.*; import java.util.zip.*; @@ -99,7 +97,7 @@ public void p2pProxyClientTest() throws Exception { .addProtocol(new HttpProtocol.Binding(unusedProxyTarget)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); Kademlia dht = builder1.getWanDht().get(); dht.bootstrapRoutingTable(node1, BootstrapTest.BOOTSTRAP_NODES, a -> true); System.out.println("Bootstrapping node..."); diff --git a/src/test/java/org/peergos/IpnsTest.java b/src/test/java/org/peergos/IpnsTest.java index 72d4d3fa..f32b5358 100644 --- a/src/test/java/org/peergos/IpnsTest.java +++ b/src/test/java/org/peergos/IpnsTest.java @@ -26,7 +26,7 @@ public void publishIPNSRecordToKubo() throws IOException { new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); Multihash node1Id = Multihash.deserialize(node1.getPeerId().getBytes()); try { @@ -73,7 +73,7 @@ public void retrieveKuboPublishedIPNS() throws IOException { new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { IPFS kubo = new IPFS("localhost", 5001); diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index 6d81b010..50879d52 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -23,13 +23,13 @@ public void findOtherNode() throws Exception { new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node2); + IdentifyBuilder.addIdentifyProtocol(node2, Collections.emptyList()); try { // bootstrap node 2 @@ -64,13 +64,13 @@ public void ipnsBenchmark() throws Exception { new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true)); Host node2 = builder2.build(); node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node2); + IdentifyBuilder.addIdentifyProtocol(node2, Collections.emptyList()); Cid value = blockstore1.put("Publish me.".getBytes(), Cid.Codec.Raw).join(); diff --git a/src/test/java/org/peergos/KuboFindProviderTest.java b/src/test/java/org/peergos/KuboFindProviderTest.java index 68d3b7f1..eac0d7c7 100644 --- a/src/test/java/org/peergos/KuboFindProviderTest.java +++ b/src/test/java/org/peergos/KuboFindProviderTest.java @@ -24,7 +24,7 @@ public void findProviderOverYamux() throws IOException { .addMuxers(List.of(StreamMuxerProtocol.getYamux())); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { IPFS kubo = new IPFS("localhost", 5001); Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); diff --git a/src/test/java/org/peergos/KuboPingTest.java b/src/test/java/org/peergos/KuboPingTest.java index ff8b00ae..da976b4f 100644 --- a/src/test/java/org/peergos/KuboPingTest.java +++ b/src/test/java/org/peergos/KuboPingTest.java @@ -26,7 +26,7 @@ public void runPingOverYamux() throws IOException { .addMuxers(List.of(StreamMuxerProtocol.getYamux())) .build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { IPFS kubo = new IPFS("localhost", 5001); Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); diff --git a/src/test/java/org/peergos/KuboTest.java b/src/test/java/org/peergos/KuboTest.java index e1041662..36b88b86 100644 --- a/src/test/java/org/peergos/KuboTest.java +++ b/src/test/java/org/peergos/KuboTest.java @@ -21,7 +21,7 @@ public void getBlock() throws IOException { Bitswap bitswap1 = new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)); Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(bitswap1)); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { IPFS kubo = new IPFS("localhost", 5001); Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); diff --git a/src/test/java/org/peergos/PingTest.java b/src/test/java/org/peergos/PingTest.java index 637a39a4..355dbcb3 100644 --- a/src/test/java/org/peergos/PingTest.java +++ b/src/test/java/org/peergos/PingTest.java @@ -62,8 +62,8 @@ public void runPingEd25519ToRSA() { new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); - IdentifyBuilder.addIdentifyProtocol(node2); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); + IdentifyBuilder.addIdentifyProtocol(node2, Collections.emptyList()); Assert.assertTrue(new Multihash(Multihash.Type.id, node1Keys.publicKey().bytes()).toString().equals(node1.getPeerId().toString())); Assert.assertTrue(new Multihash(Multihash.Type.sha2_256, Hash.sha256(node2Keys.publicKey().bytes())).toString().equals(node2.getPeerId().toString())); @@ -87,9 +87,9 @@ public void replyIdentifyOnNewDial() { Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node2); + IdentifyBuilder.addIdentifyProtocol(node2, Collections.emptyList()); try { // ping from 1 to 2 Multiaddr address2 = node2.listenAddresses().get(0); From b0adef5c5452a3e52f4a62a8e97416c00e076a02 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 16 Sep 2024 13:34:57 +0100 Subject: [PATCH 206/218] Linting --- src/main/java/org/peergos/EmbeddedIpfs.java | 8 +++----- src/test/java/org/peergos/HttpProxyTest.java | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 321127b7..c7cc0bf5 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -27,8 +27,6 @@ import org.peergos.protocol.ipns.*; import org.peergos.util.Logging; -import java.io.*; -import java.net.*; import java.nio.file.*; import java.sql.Connection; import java.sql.DriverManager; @@ -145,7 +143,7 @@ public void start() { try { this.stop().join(); } catch (Exception ex) { - ex.printStackTrace(); + LOG.info(ex.getMessage()); } }); Runtime.getRuntime().addShutdownHook(shutdownHook); @@ -179,14 +177,14 @@ public void start() { }); mdns.start(); - blockProvider.ifPresent(p -> p.start()); + blockProvider.ifPresent(PeriodicBlockProvider::start); } public CompletableFuture stop() throws Exception { if (records != null) { records.close(); } - blockProvider.ifPresent(b -> b.stop()); + blockProvider.ifPresent(PeriodicBlockProvider::stop); dht.stopBootstrapThread(); for (MDnsDiscovery m : mdns) { m.stop(); diff --git a/src/test/java/org/peergos/HttpProxyTest.java b/src/test/java/org/peergos/HttpProxyTest.java index 0e3fc26a..d470323a 100644 --- a/src/test/java/org/peergos/HttpProxyTest.java +++ b/src/test/java/org/peergos/HttpProxyTest.java @@ -77,7 +77,7 @@ public void p2pProxyRequest() throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); resp.content().readBytes(bout, resp.headers().getInt("content-length")); - Assert.assertTrue(resp.headers().get(headerName).equals(headerValue)); + Assert.assertEquals(resp.headers().get(headerName), headerValue); resp.release(); byte[] replyBody = bout.toByteArray(); equal(replyBody, httpReply); From adefb107ce862b77b19d58e9a914ad99269a2f7e Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 16 Sep 2024 14:24:44 +0100 Subject: [PATCH 207/218] v0.8.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1acd2f91..514a24fb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.9 + v0.8.0 UTF-8 From b86d80485b14ee1fdadf2f930d02dc424b265b86 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 25 Sep 2024 21:54:44 +0100 Subject: [PATCH 208/218] Guard against false positive block attacks on filtered blockstore --- .../peergos/blockstore/CidInfiniFilter.java | 7 ++- .../blockstore/FilteredBlockstore.java | 43 +++++++++++++++---- .../peergos/blockstore/s3/S3Blockstore.java | 2 +- src/main/java/org/peergos/util/Futures.java | 6 +++ 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java index 0d6327be..4ac10e83 100644 --- a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java +++ b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java @@ -35,13 +35,18 @@ public static CidInfiniFilter build(Blockstore bs) { public static CidInfiniFilter build(Blockstore bs, double falsePositiveRate) { List refs = bs.refs(false).join(); int nBlocks = refs.size()*5/4; // increase by 25% to avoid expansion during build + CidInfiniFilter infini = build(nBlocks, falsePositiveRate); + refs.forEach(c -> infini.add(c)); + return infini; + } + + public static CidInfiniFilter build(int nBlocks, double falsePositiveRate) { int nextPowerOfTwo = Math.max(17, (int) (1 + Math.log(nBlocks) / Math.log(2))); double expansionAlpha = 0.8; int bitsPerEntry = (int)(4 - Math.log(falsePositiveRate / expansionAlpha) / Math.log(2) + 1); LOG.info("Using infini filter of initial size " + ((double)(bitsPerEntry * (1 << nextPowerOfTwo) / 8) / 1024 / 1024) + " MiB"); ChainedInfiniFilter infini = new ChainedInfiniFilter(nextPowerOfTwo, bitsPerEntry); infini.set_expand_autonomously(true); - refs.forEach(c -> infini.insert(c.toBytes(), true)); return new CidInfiniFilter(infini); } } diff --git a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java index ab58a2bd..66840550 100644 --- a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java @@ -8,27 +8,48 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.*; public class FilteredBlockstore implements Blockstore { private final Blockstore blocks; - private final Filter filter; + private final Filter present; + private volatile Filter absent; + private final AtomicLong absentCount = new AtomicLong(0); - public FilteredBlockstore(Blockstore blocks, Filter filter) { + public FilteredBlockstore(Blockstore blocks, Filter present) { this.blocks = blocks; - this.filter = filter; + this.present = present; + this.absent = buildAbsentFilter(); } public CompletableFuture bloomAdd(Cid cid) { - filter.add(cid); + present.add(cid); return CompletableFuture.completedFuture(true); } + private static Filter buildAbsentFilter() { + return CidInfiniFilter.build(1_000, 0.001); + } + + private void addAbsentBlock(Cid c) { + if (absentCount.get() > 10_000) { + absentCount.set(0); + absent = buildAbsentFilter(); + } + absentCount.incrementAndGet(); + absent.add(c); + } + @Override public CompletableFuture has(Cid c) { - if (filter.has(c)) - return blocks.has(c); + if (present.has(c) && ! absent.has(c)) + return blocks.has(c).thenApply(res -> { + if (! res) + addAbsentBlock(c); + return false; + }); return CompletableFuture.completedFuture(false); } @@ -40,15 +61,19 @@ public CompletableFuture hasAny(Multihash h) { @Override public CompletableFuture> get(Cid c) { - if (filter.has(c)) - return blocks.get(c); + if (present.has(c) && ! absent.has(c)) { + return blocks.get(c).exceptionally(t -> { + addAbsentBlock(c); + return Optional.empty(); + }); + } return CompletableFuture.completedFuture(Optional.empty()); } @Override public CompletableFuture put(byte[] block, Cid.Codec codec) { return blocks.put(block, codec) - .thenApply(filter::add); + .thenApply(present::add); } @Override diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index aff3b92d..cdbaf118 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -349,7 +349,7 @@ private CompletableFuture> getWithoutRetry(Cid cid) { LOG.log(Level.WARNING, msg, e); } failedBlockGets.inc(); - throw new RuntimeException(e.getMessage(), e); + return Futures.errored(e); } finally { readTimer.observeDuration(); } diff --git a/src/main/java/org/peergos/util/Futures.java b/src/main/java/org/peergos/util/Futures.java index 83b55de0..1a264d29 100755 --- a/src/main/java/org/peergos/util/Futures.java +++ b/src/main/java/org/peergos/util/Futures.java @@ -19,4 +19,10 @@ private static T logAndThrow(Throwable t, Optional message) { t.printStackTrace(); throw new RuntimeException(t.getMessage(), t); } + + public static CompletableFuture errored(Throwable t) { + CompletableFuture err = new CompletableFuture<>(); + err.completeExceptionally(t); + return err; + } } From df1324b29800fe3ba4398a995c7a33dfff72bede Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 28 Sep 2024 20:18:50 +0100 Subject: [PATCH 209/218] Make sure we close connections in kademlia after queries --- .../org/peergos/protocol/dht/Kademlia.java | 69 +++++++++++++------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 6a39c0c2..e4bc969c 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -115,12 +115,12 @@ public void bootstrap(Host us) { // lookup our own peer id to keep our nearest neighbours up-to-date, // and connect to all of them, so they know about our addresses List closestToUs = findClosestPeers(Multihash.deserialize(us.getPeerId().getBytes()), 20, us); - int connectedClosest = 0; + List connected = new ArrayList<>(); for (PeerAddresses peer : closestToUs) { if (connectTo(us, peer)) - connectedClosest++; + connected.add(peer.peerId); } - LOG.info("Bootstrap connected to " + connectedClosest + " nodes close to us."); + LOG.info("Bootstrap connected to " + connected.size() + " nodes close to us. " + connected.stream().map(Multihash::toString).sorted().limit(5).collect(Collectors.toList())); } static class RoutingEntry { @@ -233,12 +233,16 @@ public CompletableFuture> findProviders(Multihash block, Hos List> futures = queryThisRound.stream() .parallel() .map(r -> { - KademliaController res = null; + StreamPromise conn = null; try { - res = dialPeer(r.addresses, us).join(); - return res.getProviders(block).orTimeout(2, TimeUnit.SECONDS); - }catch (Exception e) { + conn = dialPeer(r.addresses, us); + return conn.getController().join() + .getProviders(block).orTimeout(2, TimeUnit.SECONDS); + } catch (Exception e) { return null; + } finally { + if (conn != null) + conn.getStream().thenApply(s -> s.close()); } }).filter(prov -> prov != null) .collect(Collectors.toList()); @@ -269,8 +273,11 @@ public CompletableFuture> findProviders(Multihash block, Hos } private CompletableFuture> getCloserPeers(byte[] key, PeerAddresses target, Host us) { + StreamPromise conn = null; try { - return dialPeer(target, us).orTimeout(2, TimeUnit.SECONDS).join().closerPeers(key); + conn = dialPeer(target, us); + KademliaController contr = conn.getController().orTimeout(2, TimeUnit.SECONDS).join(); + return contr.closerPeers(key); } catch (Exception e) { // we can't dial quic only nodes until it's implemented if (target.addresses.stream().allMatch(a -> a.toString().contains("quic"))) @@ -282,6 +289,9 @@ private CompletableFuture> getCloserPeers(byte[] key, PeerAd else if (e.getCause() instanceof ConnectionClosedException) {} else e.printStackTrace(); + } finally { + if (conn != null) + conn.getStream().thenApply(s -> s.close()); } return CompletableFuture.completedFuture(Collections.emptyList()); } @@ -292,26 +302,34 @@ private Multiaddr[] getPublic(PeerAddresses target) { .collect(Collectors.toList()).toArray(new Multiaddr[0]); } - private CompletableFuture dialPeer(PeerAddresses target, Host us) { + private StreamPromise dialPeer(PeerAddresses target, Host us) { Multiaddr[] multiaddrs = target.addresses.stream() .map(a -> Multiaddr.fromString(a.toString())) .filter(a -> ! a.has(Protocol.DNS) && ! a.has(Protocol.DNS4) && ! a.has(Protocol.DNS6)) .collect(Collectors.toList()).toArray(new Multiaddr[0]); - return dial(us, PeerId.fromBase58(target.peerId.toBase58()), multiaddrs).getController(); + return dial(us, PeerId.fromBase58(target.peerId.toBase58()), multiaddrs); } public CompletableFuture provideBlock(Multihash block, Host us, PeerAddresses ourAddrs) { List closestPeers = findClosestPeers(block, 20, us); List> provides = closestPeers.stream() .parallel() - .map(p -> dialPeer(p, us) - .thenCompose(contr -> contr.provide(block, ourAddrs)) - .exceptionally(t -> { - if (t.getCause() instanceof NonCompleteException) + .map(p -> { + StreamPromise conn = dialPeer(p, us); + return conn.getController() + .thenCompose(contr -> contr.provide(block, ourAddrs)) + .thenApply(res -> { + conn.getStream().thenApply(s -> s.close()); + return res; + }) + .exceptionally(t -> { + if (t.getCause() instanceof NonCompleteException) + return true; + LOG.log(Level.FINE, t, t::getMessage); + conn.getStream().thenApply(s -> s.close()); return true; - LOG.log(Level.FINE, t, t::getMessage); - return true; - })) + }); + }) .collect(Collectors.toList()); return CompletableFuture.allOf(provides.toArray(new CompletableFuture[0])); } @@ -333,10 +351,15 @@ private CompletableFuture putValue(Multihash publisher, byte[] signedRecord, PeerAddresses peer, Host us) { + StreamPromise conn = null; try { - return dialPeer(peer, us).join() + conn = dialPeer(peer, us); + return conn.getController().join() .putValue(publisher, signedRecord); - } catch (Exception e) {} + } catch (Exception e) {} finally { + if (conn != null) + conn.getStream().thenApply(s -> s.close()); + } return CompletableFuture.completedFuture(false); } @@ -446,8 +469,11 @@ public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, } private CompletableFuture> getValueFromPeer(PeerAddresses peer, Multihash publisher, Host us) { + StreamPromise conn = null; try { - return dialPeer(peer, us) + conn = dialPeer(peer, us); + return conn + .getController() .orTimeout(1, TimeUnit.SECONDS) .join() .getValue(publisher) @@ -455,6 +481,9 @@ private CompletableFuture> getValueFromPeer(PeerAddresses pe .thenApply(Optional::of); } catch (Exception e) { return CompletableFuture.completedFuture(Optional.empty()); + } finally { + if (conn != null) + conn.getStream().thenApply(s -> s.close()); } } public List resolveValue(Multihash publisher, int minResults, Host us) { From 7c67445a49d888065aa751aae9a17e1decf1f978 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Sep 2024 08:59:51 +0100 Subject: [PATCH 210/218] Close entire connection,not just stream in kademlia. --- .../org/peergos/protocol/dht/Kademlia.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index e4bc969c..5dac139e 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -95,7 +95,13 @@ public void stopBootstrapThread() { private boolean connectTo(Host us, PeerAddresses peer) { try { - new Identify().dial(us, PeerId.fromBase58(peer.peerId.toBase58()), getPublic(peer)).getController().join().id().join(); + PeerId ourPeerId = PeerId.fromBase58(peer.peerId.toBase58()); + StreamPromise conn = new Identify().dial(us, ourPeerId, getPublic(peer)); + try { + conn.getController().join().id().join(); + } finally { + conn.getStream().thenApply(s -> s.getConnection().close()); + } return true; } catch (Exception e) { if (e.getCause() instanceof NothingToCompleteException || e.getCause() instanceof NonCompleteException) @@ -242,7 +248,7 @@ public CompletableFuture> findProviders(Multihash block, Hos return null; } finally { if (conn != null) - conn.getStream().thenApply(s -> s.close()); + conn.getStream().thenApply(s -> s.getConnection().close()); } }).filter(prov -> prov != null) .collect(Collectors.toList()); @@ -291,7 +297,7 @@ else if (e.getCause() instanceof ConnectionClosedException) {} e.printStackTrace(); } finally { if (conn != null) - conn.getStream().thenApply(s -> s.close()); + conn.getStream().thenApply(s -> s.getConnection().close()); } return CompletableFuture.completedFuture(Collections.emptyList()); } @@ -319,14 +325,14 @@ public CompletableFuture provideBlock(Multihash block, Host us, PeerAddres return conn.getController() .thenCompose(contr -> contr.provide(block, ourAddrs)) .thenApply(res -> { - conn.getStream().thenApply(s -> s.close()); + conn.getStream().thenApply(s -> s.getConnection().close()); return res; }) .exceptionally(t -> { if (t.getCause() instanceof NonCompleteException) return true; LOG.log(Level.FINE, t, t::getMessage); - conn.getStream().thenApply(s -> s.close()); + conn.getStream().thenApply(s -> s.getConnection().close()); return true; }); }) @@ -358,7 +364,7 @@ private CompletableFuture putValue(Multihash publisher, .putValue(publisher, signedRecord); } catch (Exception e) {} finally { if (conn != null) - conn.getStream().thenApply(s -> s.close()); + conn.getStream().thenApply(s -> s.getConnection().close()); } return CompletableFuture.completedFuture(false); } @@ -483,7 +489,7 @@ private CompletableFuture> getValueFromPeer(PeerAddresses pe return CompletableFuture.completedFuture(Optional.empty()); } finally { if (conn != null) - conn.getStream().thenApply(s -> s.close()); + conn.getStream().thenApply(s -> s.getConnection().close()); } } public List resolveValue(Multihash publisher, int minResults, Host us) { From 89f4ac51d475cf8cb5f5ac07c8a62b7005f5ac1f Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Sep 2024 09:30:43 +0100 Subject: [PATCH 211/218] Close incoming kademlia connections too! --- src/main/java/org/peergos/protocol/dht/KademliaEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 62fe6c2d..a82c26fa 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -198,6 +198,7 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { case PING: {break;} // Not used any more default: throw new IllegalStateException("Unknown message kademlia type: " + msg.getType()); } + stream.getConnection().close(); } public static boolean isPublic(Multiaddr addr) { From 1652693541b7d6bc25e8978dd079c3942081f91a Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Sep 2024 11:02:26 +0100 Subject: [PATCH 212/218] Only close kademlia streams This is to not interfere with other protocols. Then we can close connections with no open streams. --- .../org/peergos/protocol/dht/Kademlia.java | 22 +++++++++++++------ .../peergos/protocol/dht/KademliaEngine.java | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 5dac139e..a4f526e3 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -100,7 +100,7 @@ private boolean connectTo(Host us, PeerAddresses peer) { try { conn.getController().join().id().join(); } finally { - conn.getStream().thenApply(s -> s.getConnection().close()); + conn.getStream().thenApply(s -> s.close()); } return true; } catch (Exception e) { @@ -127,6 +127,14 @@ public void bootstrap(Host us) { connected.add(peer.peerId); } LOG.info("Bootstrap connected to " + connected.size() + " nodes close to us. " + connected.stream().map(Multihash::toString).sorted().limit(5).collect(Collectors.toList())); + + List allConns = us.getNetwork().getConnections(); + Set activeConns = us.getStreams().stream().map(s -> s.getConnection()).collect(Collectors.toSet()); + List toClose = allConns.stream().filter(c -> !activeConns.contains(c)).collect(Collectors.toList()); + LOG.info("Closing " + toClose.size() + " / " + allConns.size() + " connections..."); + for (Connection conn : toClose) { + conn.close(); + } } static class RoutingEntry { @@ -248,7 +256,7 @@ public CompletableFuture> findProviders(Multihash block, Hos return null; } finally { if (conn != null) - conn.getStream().thenApply(s -> s.getConnection().close()); + conn.getStream().thenApply(s -> s.close()); } }).filter(prov -> prov != null) .collect(Collectors.toList()); @@ -297,7 +305,7 @@ else if (e.getCause() instanceof ConnectionClosedException) {} e.printStackTrace(); } finally { if (conn != null) - conn.getStream().thenApply(s -> s.getConnection().close()); + conn.getStream().thenApply(s -> s.close()); } return CompletableFuture.completedFuture(Collections.emptyList()); } @@ -325,14 +333,14 @@ public CompletableFuture provideBlock(Multihash block, Host us, PeerAddres return conn.getController() .thenCompose(contr -> contr.provide(block, ourAddrs)) .thenApply(res -> { - conn.getStream().thenApply(s -> s.getConnection().close()); + conn.getStream().thenApply(s -> s.close()); return res; }) .exceptionally(t -> { if (t.getCause() instanceof NonCompleteException) return true; LOG.log(Level.FINE, t, t::getMessage); - conn.getStream().thenApply(s -> s.getConnection().close()); + conn.getStream().thenApply(s -> s.close()); return true; }); }) @@ -364,7 +372,7 @@ private CompletableFuture putValue(Multihash publisher, .putValue(publisher, signedRecord); } catch (Exception e) {} finally { if (conn != null) - conn.getStream().thenApply(s -> s.getConnection().close()); + conn.getStream().thenApply(s -> s.close()); } return CompletableFuture.completedFuture(false); } @@ -489,7 +497,7 @@ private CompletableFuture> getValueFromPeer(PeerAddresses pe return CompletableFuture.completedFuture(Optional.empty()); } finally { if (conn != null) - conn.getStream().thenApply(s -> s.getConnection().close()); + conn.getStream().thenApply(s -> s.close()); } } public List resolveValue(Multihash publisher, int minResults, Host us) { diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index a82c26fa..6eb164eb 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -198,7 +198,7 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { case PING: {break;} // Not used any more default: throw new IllegalStateException("Unknown message kademlia type: " + msg.getType()); } - stream.getConnection().close(); + stream.close(); } public static boolean isPublic(Multiaddr addr) { From eb35b5a3cf9e21435c9487656bd97f327fe2addf Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Sep 2024 16:21:31 +0100 Subject: [PATCH 213/218] Tolerate multiaddrs we can't parse in handling kademlia responses. --- src/main/java/org/peergos/PeerAddresses.java | 9 ++++- .../org/peergos/protocol/dht/Kademlia.java | 34 +++++++++++-------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/peergos/PeerAddresses.java b/src/main/java/org/peergos/PeerAddresses.java index f2afb379..c2efe8c8 100644 --- a/src/main/java/org/peergos/PeerAddresses.java +++ b/src/main/java/org/peergos/PeerAddresses.java @@ -15,6 +15,7 @@ import java.util.*; import java.util.function.*; import java.util.stream.*; +import java.util.stream.Stream; public class PeerAddresses { public final Multihash peerId; @@ -57,7 +58,13 @@ public static PeerAddresses fromProtobuf(Dht.Message.Peer peer) { Multihash peerId = Multihash.deserialize(peer.getId().toByteArray()); List addrs = peer.getAddrsList() .stream() - .map(b -> Multiaddr.deserialize(b.toByteArray())) + .flatMap(b -> { + try { + return Stream.of(Multiaddr.deserialize(b.toByteArray())); + } catch (Exception e) { + return Stream.empty(); + } + }) .collect(Collectors.toList()); return new PeerAddresses(peerId, addrs); } diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index a4f526e3..7d474de1 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -131,12 +131,24 @@ public void bootstrap(Host us) { List allConns = us.getNetwork().getConnections(); Set activeConns = us.getStreams().stream().map(s -> s.getConnection()).collect(Collectors.toSet()); List toClose = allConns.stream().filter(c -> !activeConns.contains(c)).collect(Collectors.toList()); - LOG.info("Closing " + toClose.size() + " / " + allConns.size() + " connections..."); for (Connection conn : toClose) { conn.close(); } } + private static CompletableFuture closeAfter(CompletableFuture sf, Supplier> query) { + CompletableFuture res = new CompletableFuture<>(); + query.get().thenAccept(v -> { + sf.thenAccept(s -> s.close()); + res.complete(v); + }).exceptionally(t -> { + sf.thenAccept(s -> s.close()); + res.completeExceptionally(t); + return null; + }); + return res; + } + static class RoutingEntry { public final Id key; public final PeerAddresses addresses; @@ -287,11 +299,10 @@ public CompletableFuture> findProviders(Multihash block, Hos } private CompletableFuture> getCloserPeers(byte[] key, PeerAddresses target, Host us) { - StreamPromise conn = null; try { - conn = dialPeer(target, us); + StreamPromise conn = dialPeer(target, us); KademliaController contr = conn.getController().orTimeout(2, TimeUnit.SECONDS).join(); - return contr.closerPeers(key); + return closeAfter(conn.getStream(), () -> contr.closerPeers(key)); } catch (Exception e) { // we can't dial quic only nodes until it's implemented if (target.addresses.stream().allMatch(a -> a.toString().contains("quic"))) @@ -303,9 +314,6 @@ private CompletableFuture> getCloserPeers(byte[] key, PeerAd else if (e.getCause() instanceof ConnectionClosedException) {} else e.printStackTrace(); - } finally { - if (conn != null) - conn.getStream().thenApply(s -> s.close()); } return CompletableFuture.completedFuture(Collections.emptyList()); } @@ -365,15 +373,11 @@ private CompletableFuture putValue(Multihash publisher, byte[] signedRecord, PeerAddresses peer, Host us) { - StreamPromise conn = null; try { - conn = dialPeer(peer, us); - return conn.getController().join() - .putValue(publisher, signedRecord); - } catch (Exception e) {} finally { - if (conn != null) - conn.getStream().thenApply(s -> s.close()); - } + StreamPromise conn = dialPeer(peer, us); + return closeAfter(conn.getStream(), () -> conn.getController().join() + .putValue(publisher, signedRecord)); + } catch (Exception e) {} return CompletableFuture.completedFuture(false); } From c359692705a859609e97ed91b10c1d3ccabb87ba Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Sep 2024 16:22:13 +0100 Subject: [PATCH 214/218] Tolerate cid codecs we don't understand in bitswap queries --- .../java/org/peergos/protocol/bitswap/BitswapEngine.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 3e9927c8..fb4ddb9c 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -158,7 +158,12 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter int presentBlocks = 0; if (msg.hasWantlist()) { for (MessageOuterClass.Message.Wantlist.Entry e : msg.getWantlist().getEntriesList()) { - Cid c = Cid.cast(e.getBlock().toByteArray()); + Cid c; + try { + c = Cid.cast(e.getBlock().toByteArray()); + } catch (Exception ex) { + continue; + } Optional auth = e.getAuth().isEmpty() ? Optional.empty() : Optional.of(ArrayOps.bytesToHex(e.getAuth().toByteArray())); boolean isCancel = e.getCancel(); boolean sendDontHave = e.getSendDontHave(); From d9018ae83161cbb4a6ff118752396cb25de29035 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 30 Sep 2024 22:40:11 +0100 Subject: [PATCH 215/218] Fix FilteredBlockstore.has (I'm a clown) Add a test for it too. --- src/main/java/org/peergos/blockstore/FilteredBlockstore.java | 2 +- src/test/java/org/peergos/blockstore/BloomTest.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java index 66840550..e2ef0adc 100644 --- a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java @@ -48,7 +48,7 @@ public CompletableFuture has(Cid c) { return blocks.has(c).thenApply(res -> { if (! res) addAbsentBlock(c); - return false; + return res; }); return CompletableFuture.completedFuture(false); } diff --git a/src/test/java/org/peergos/blockstore/BloomTest.java b/src/test/java/org/peergos/blockstore/BloomTest.java index 6ef62c5f..5ffa771c 100644 --- a/src/test/java/org/peergos/blockstore/BloomTest.java +++ b/src/test/java/org/peergos/blockstore/BloomTest.java @@ -33,6 +33,10 @@ public void bloom() { long t4 = System.currentTimeMillis(); System.out.println("Doubling filter size took: " + (t4-t3)+ "ms"); + for (Cid ref : bs.refs(false).join()) { + Assert.assertTrue(filtered.has(ref).join()); + } + checkFalsePositiveRate(bloom, 14); } From 8fb1d0527f920ec06f2f7b9d737eb29810fbace6 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 7 Oct 2024 09:46:09 +0100 Subject: [PATCH 216/218] Fix p2p http memory leak Add failing test for it --- .../peergos/protocol/http/HttpProtocol.java | 21 +++++-- .../java/org/peergos/EmbeddedIpfsTest.java | 63 ++++++++++++++++++- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/peergos/protocol/http/HttpProtocol.java b/src/main/java/org/peergos/protocol/http/HttpProtocol.java index 217c5f43..9d76affe 100644 --- a/src/main/java/org/peergos/protocol/http/HttpProtocol.java +++ b/src/main/java/org/peergos/protocol/http/HttpProtocol.java @@ -46,9 +46,11 @@ public Sender(Stream stream) { @Override public void onMessage(@NotNull Stream stream, FullHttpResponse msg) { CompletableFuture req = queue.poll(); - if (req != null) - req.complete(msg.copy()); - msg.release(); + if (req != null) { + req.complete(msg.retain()); + req.thenAccept(x -> msg.release()); + } else + msg.release(); stream.close(); } @@ -86,7 +88,7 @@ public Receiver(HttpRequestProcessor requestHandler) { } private void sendReply(HttpContent reply, Stream p2pstream) { - p2pstream.writeAndFlush(reply.copy()); + p2pstream.writeAndFlush(reply.retain()); } @Override @@ -120,7 +122,16 @@ protected void initChannel(Channel ch) throws Exception { Channel ch = fut.channel(); FullHttpRequest retained = msg.retain(); - fut.addListener(x -> ch.writeAndFlush(retained)); + fut.addListener(x -> { + if (x.isSuccess()) + ch.writeAndFlush(retained).addListener(f -> { + if (!f.isSuccess()) { + retained.release(); + } + }); + else + retained.release(); + }); } private static final long TRAFFIC_LIMIT = Long.MAX_VALUE; // This is the total inbound or outbound traffic allowed, not a rate diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index dc15bfc4..54549808 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -1,5 +1,6 @@ package org.peergos; +import com.sun.net.httpserver.HttpServer; import identify.pb.*; import io.ipfs.cid.*; import io.ipfs.multiaddr.*; @@ -9,12 +10,22 @@ import io.libp2p.core.multiformats.*; import io.libp2p.crypto.keys.*; import io.libp2p.protocol.*; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; import org.junit.*; import org.peergos.blockstore.*; import org.peergos.config.*; import org.peergos.protocol.dht.*; +import org.peergos.protocol.http.HttpProtocol; import org.peergos.protocol.ipns.*; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; import java.time.*; import java.util.*; import java.util.concurrent.*; @@ -43,6 +54,52 @@ public void largeBlock() throws Exception { node2.stop(); } + @Test + public void largeWrite() throws Exception { + System.setProperty("io.netty.leakDetection.level", "advanced"); + // Start proxy target + InetSocketAddress proxyTarget = new InetSocketAddress("localhost", 7777); + HttpServer target = HttpServer.create(proxyTarget, 20); + String reply = "AllGood"; + byte[] replyBytes = reply.getBytes(); + target.createContext("/", ex -> { + ex.sendResponseHeaders(200, replyBytes.length); + OutputStream out = ex.getResponseBody(); + out.write(replyBytes); + out.flush(); + out.close(); + }); + target.start(); + + HttpProtocol.HttpRequestProcessor http1 = (s, req, h) -> HttpProtocol.proxyRequest(req, proxyTarget, h); + EmbeddedIpfs node1 = build(Collections.emptyList(), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort())), Optional.of(http1)); + node1.start(); + + HttpProtocol.HttpRequestProcessor http2 = (s, req, h) -> HttpProtocol.proxyRequest(req, new InetSocketAddress("localhost", 7778), h); + EmbeddedIpfs node2 = build(node1.node.listenAddresses() + .stream() + .map(a -> new MultiAddress(a.toString())) + .collect(Collectors.toList()), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort())), Optional.of(http2)); + node2.start(); + + for (int i = 0; i < 100; i++) { + ByteBuf largeBody = Unpooled.buffer(2 * 1024 * 1024); + DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/hey", largeBody); + HttpProtocol.HttpController http = node2.p2pHttp.get().dial(node2.node, node1.node.getPeerId(), node1.node.listenAddresses().toArray(Multiaddr[]::new)) + .getController().join(); + FullHttpResponse resp = http.send(req).join(); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + int contentLength = resp.headers().getInt("content-length"); + resp.content().readBytes(bout, contentLength); + byte[] body = bout.toByteArray(); + Assert.assertTrue("Correct response", Arrays.equals(body, replyBytes)); + resp.release(); + } + + node1.stop(); + node2.stop(); + } + @Test public void mdnsDiscovery() throws Exception { EmbeddedIpfs node1 = build(Collections.emptyList(), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); @@ -138,12 +195,16 @@ public void wildcardListenerAddressesGetExpanded() { } public static EmbeddedIpfs build(List bootstrap, List swarmAddresses) { + return build(bootstrap, swarmAddresses, Optional.empty()); + } + + public static EmbeddedIpfs build(List bootstrap, List swarmAddresses, Optional http) { BlockRequestAuthoriser blockRequestAuthoriser = (c, p, a) -> CompletableFuture.completedFuture(true); HostBuilder builder = new HostBuilder().generateIdentity(); PrivKey privKey = builder.getPrivateKey(); PeerId peerId = builder.getPeerId(); IdentitySection id = new IdentitySection(privKey.bytes(), peerId); return EmbeddedIpfs.build(new RamRecordStore(), new RamBlockstore(), true, swarmAddresses, bootstrap, - id, blockRequestAuthoriser, Optional.empty()); + id, blockRequestAuthoriser, http); } } From 0290ddd7d05594e7114a6d210e42a3bd920e1a5d Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 7 Oct 2024 15:55:23 +0100 Subject: [PATCH 217/218] More p2p http ref count fixes --- .../java/org/peergos/protocol/http/HttpProtocol.java | 11 +++-------- src/test/java/org/peergos/EmbeddedIpfsTest.java | 3 ++- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/peergos/protocol/http/HttpProtocol.java b/src/main/java/org/peergos/protocol/http/HttpProtocol.java index 9d76affe..2d3fd031 100644 --- a/src/main/java/org/peergos/protocol/http/HttpProtocol.java +++ b/src/main/java/org/peergos/protocol/http/HttpProtocol.java @@ -1,8 +1,5 @@ package org.peergos.protocol.http; -import io.ipfs.cid.*; -import io.ipfs.multibase.*; -import io.ipfs.multihash.*; import io.libp2p.core.*; import io.libp2p.core.multistream.*; import io.libp2p.protocol.*; @@ -48,9 +45,9 @@ public void onMessage(@NotNull Stream stream, FullHttpResponse msg) { CompletableFuture req = queue.poll(); if (req != null) { req.complete(msg.retain()); - req.thenAccept(x -> msg.release()); - } else + } else { msg.release(); + } stream.close(); } @@ -125,9 +122,7 @@ protected void initChannel(Channel ch) throws Exception { fut.addListener(x -> { if (x.isSuccess()) ch.writeAndFlush(retained).addListener(f -> { - if (!f.isSuccess()) { - retained.release(); - } + retained.release(); }); else retained.release(); diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 54549808..f06a3165 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -82,7 +82,7 @@ public void largeWrite() throws Exception { .collect(Collectors.toList()), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort())), Optional.of(http2)); node2.start(); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 1000; i++) { ByteBuf largeBody = Unpooled.buffer(2 * 1024 * 1024); DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/hey", largeBody); HttpProtocol.HttpController http = node2.p2pHttp.get().dial(node2.node, node1.node.getPeerId(), node1.node.listenAddresses().toArray(Multiaddr[]::new)) @@ -94,6 +94,7 @@ public void largeWrite() throws Exception { byte[] body = bout.toByteArray(); Assert.assertTrue("Correct response", Arrays.equals(body, replyBytes)); resp.release(); + resp.release(); } node1.stop(); From 1e594114103848fa957ca65c1586efbae3fa1882 Mon Sep 17 00:00:00 2001 From: Yordan Atanasov Date: Thu, 19 Dec 2024 18:01:53 +0200 Subject: [PATCH 218/218] Merge upstream --- .github/workflows/maven.yml | 4 +- README.md | 2 +- pom.xml | 103 ++++----- src/main/java/org/peergos/EmbeddedIpfs.java | 122 ++++++++--- src/main/java/org/peergos/HostBuilder.java | 62 +++--- src/main/java/org/peergos/IpnsPublisher.java | 50 +++-- src/main/java/org/peergos/PeerAddresses.java | 9 +- src/main/java/org/peergos/RamAddressBook.java | 29 ++- .../peergos/blockstore/CidInfiniFilter.java | 7 +- .../blockstore/FilteredBlockstore.java | 43 +++- .../peergos/blockstore/s3/S3Blockstore.java | 63 ++++-- src/main/java/org/peergos/net/APIHandler.java | 62 +++--- .../org/peergos/protocol/IdentifyBuilder.java | 4 +- .../protocol/bitswap/BitswapEngine.java | 7 +- .../org/peergos/protocol/dht/Kademlia.java | 200 ++++++++++++------ .../peergos/protocol/dht/KademliaEngine.java | 1 + .../peergos/protocol/http/HttpProtocol.java | 22 +- src/main/java/org/peergos/util/Futures.java | 6 + src/test/java/org/peergos/APIServiceTest.java | 4 +- .../java/org/peergos/BitswapMirrorTest.java | 4 +- src/test/java/org/peergos/BitswapTest.java | 49 +++-- src/test/java/org/peergos/BootstrapTest.java | 29 +-- .../java/org/peergos/EmbeddedIpfsTest.java | 130 ++++++++++-- src/test/java/org/peergos/FindPeerTest.java | 6 +- .../java/org/peergos/FindProviderTest.java | 2 +- src/test/java/org/peergos/HandlerTest.java | 28 ++- src/test/java/org/peergos/HttpProxyTest.java | 76 ++++--- src/test/java/org/peergos/IpnsTest.java | 21 +- src/test/java/org/peergos/KademliaTest.java | 66 +++--- .../org/peergos/KuboFindProviderTest.java | 4 +- src/test/java/org/peergos/KuboPingTest.java | 2 +- src/test/java/org/peergos/KuboTest.java | 2 +- .../java/org/peergos/P2pHttpChatTest.java | 5 +- src/test/java/org/peergos/P2pHttpTest.java | 4 +- src/test/java/org/peergos/PingTest.java | 43 ++-- src/test/java/org/peergos/ProvideTest.java | 27 +-- src/test/java/org/peergos/RelayTest.java | 33 +-- .../org/peergos/blockstore/BloomTest.java | 4 + 38 files changed, 870 insertions(+), 465 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 96c7d573..2b6a211b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/actions/local-s3 - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: temurin java-version: 11 diff --git a/README.md b/README.md index 9ef6349c..fadf89d1 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Currently implemented properties: * bloom/[infini filtered](https://www.rasmuspagh.net/papers/infinifilter.pdf) blockstore * connect bitswap to kademlia for discovery, with a faster version with supplied peerids * configurable cid publishing function +* mDNS peer discovery * Android compatibility * example serverless chat app using p2p http proxy for Android * interop tests with other implementations - https://github.com/libp2p/test-plans/ @@ -37,7 +38,6 @@ In the future we will add: * circuit-relay * dcutr (direct connection upgrade through relay) * AutoRelay -* mDNS peer discovery * example iOS chat app * QUIC transport (and encryption and multiplexing) diff --git a/pom.xml b/pom.xml index 4bcfd4b4..19584575 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.peergos nabu - v0.7.9 + v0.8.0 UTF-8 @@ -16,7 +16,7 @@ 2.2 v1.4.12 v1.4.4 - 1.9.10 + 2.1.0 @@ -40,35 +40,31 @@ - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - true - org.apache.maven.plugins - maven-dependency-plugin - 2.6 + maven-assembly-plugin + 3.1.1 + + + + jar-with-dependencies + + + - unpack-dependencies + make-assembly package - unpack-dependencies + single - - system - META-INF/*.SF,META-INF/*.DSA,META-INF/*.RSA - ${project.build.directory}/classes - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} compile @@ -76,60 +72,51 @@ compile + + + src/main/java + target/generated-sources/annotations + + - testCompile + test-compile test-compile - testCompile + test-compile + + ${maven.compiler.target} + org.apache.maven.plugins - maven-assembly-plugin - 3.1.1 - - - - jar-with-dependencies - - - + maven-compiler-plugin + 3.10.1 - make-assembly - package + compile + compile - single + compile - - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 - - package + testCompile + test-compile - shade + testCompile - - false - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - + + + default-compile + none + + + default-testCompile + none @@ -139,7 +126,7 @@ org.jetbrains.kotlin - kotlin-stdlib + kotlin-stdlib-jdk8 ${kotlin.version} @@ -162,7 +149,7 @@ com.github.peergos jvm-libp2p - 0.16.6 + 0.18.0-ipv6-mdns-wildcard org.slf4j diff --git a/src/main/java/org/peergos/EmbeddedIpfs.java b/src/main/java/org/peergos/EmbeddedIpfs.java index 1050b8b6..83bf987b 100644 --- a/src/main/java/org/peergos/EmbeddedIpfs.java +++ b/src/main/java/org/peergos/EmbeddedIpfs.java @@ -1,40 +1,64 @@ package org.peergos; -import io.ipfs.cid.*; -import io.ipfs.multiaddr.*; +import io.ipfs.cid.Cid; +import io.ipfs.multiaddr.MultiAddress; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import io.libp2p.core.crypto.*; -import io.libp2p.core.multiformats.*; -import io.libp2p.core.multistream.*; -import io.libp2p.protocol.*; -import org.peergos.blockstore.*; +import io.libp2p.core.AddressBook; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.crypto.PubKey; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.core.multistream.ProtocolBinding; +import io.libp2p.discovery.MDnsDiscovery; +import io.libp2p.protocol.Ping; +import org.peergos.blockstore.Blockstore; +import org.peergos.blockstore.FileBlockstore; +import org.peergos.blockstore.FilteredBlockstore; +import org.peergos.blockstore.ProvidingBlockstore; +import org.peergos.blockstore.TypeLimitedBlockstore; import org.peergos.blockstore.metadatadb.BlockMetadataStore; -import org.peergos.blockstore.metadatadb.JdbcBlockMetadataStore; import org.peergos.blockstore.metadatadb.CachingBlockMetadataStore; +import org.peergos.blockstore.metadatadb.JdbcBlockMetadataStore; import org.peergos.blockstore.metadatadb.sql.H2BlockMetadataCommands; import org.peergos.blockstore.metadatadb.sql.UncloseableConnection; import org.peergos.blockstore.s3.S3Blockstore; -import org.peergos.config.*; +import org.peergos.config.Config; +import org.peergos.config.FilterType; +import org.peergos.config.IdentitySection; import org.peergos.net.ConnectionException; -import org.peergos.protocol.*; -import org.peergos.protocol.autonat.*; -import org.peergos.protocol.bitswap.*; -import org.peergos.protocol.circuit.*; -import org.peergos.protocol.dht.*; -import org.peergos.protocol.http.*; -import org.peergos.protocol.ipns.*; +import org.peergos.protocol.IdentifyBuilder; +import org.peergos.protocol.autonat.AutonatProtocol; +import org.peergos.protocol.bitswap.Bitswap; +import org.peergos.protocol.bitswap.BitswapEngine; +import org.peergos.protocol.circuit.CircuitHopProtocol; +import org.peergos.protocol.circuit.CircuitStopProtocol; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.KademliaController; +import org.peergos.protocol.dht.KademliaEngine; +import org.peergos.protocol.dht.ProviderStore; +import org.peergos.protocol.dht.RamProviderStore; +import org.peergos.protocol.dht.RecordStore; +import org.peergos.protocol.http.HttpProtocol; +import org.peergos.protocol.ipns.IPNS; +import org.peergos.protocol.ipns.IpnsRecord; import org.peergos.util.Logging; -import java.nio.file.*; +import java.nio.file.Path; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; -import java.time.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; -import java.util.stream.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; +import java.util.stream.Collectors; public class EmbeddedIpfs { private static final Logger LOG = Logging.LOG(); @@ -49,6 +73,8 @@ public class EmbeddedIpfs { public final Optional p2pHttp; private final List bootstrap; private final Optional blockProvider; + private final List announce; + private final List mdns = new ArrayList<>(); public EmbeddedIpfs(Host node, Blockstore blockstore, @@ -57,7 +83,8 @@ public EmbeddedIpfs(Host node, Bitswap bitswap, Optional p2pHttp, List bootstrap, - Optional> newBlockProvider) { + Optional> newBlockProvider, + List announce) { this.node = node; this.blockstore = blockstore; this.records = records; @@ -68,6 +95,7 @@ public EmbeddedIpfs(Host node, this.blocks = new BitswapBlockService(node, bitswap, dht); this.blockProvider = newBlockProvider.map(q -> new PeriodicBlockProvider(22 * 3600_000L, () -> blockstore.refs(false).join().stream(), node, dht, q)); + this.announce = announce; } public int maxBlockSize() { @@ -138,12 +166,15 @@ public void start() { try { this.stop().join(); } catch (Exception ex) { - ex.printStackTrace(); + LOG.info(ex.getMessage()); } }); Runtime.getRuntime().addShutdownHook(shutdownHook); node.start().join(); - IdentifyBuilder.addIdentifyProtocol(node); + IdentifyBuilder.addIdentifyProtocol(node, announce.stream() + .map(MultiAddress::toString) + .map(Multiaddr::new) + .collect(Collectors.toList())); LOG.info("Node started and listening on " + node.listenAddresses()); LOG.info("Bootstrapping IPFS routing table"); if (bootstrap.isEmpty()) @@ -153,15 +184,34 @@ public void start() { dht.bootstrap(node); dht.startBootstrapThread(node); - blockProvider.ifPresent(p -> p.start()); + LOG.info("MDNS discovery enabled"); + MDnsDiscovery mdns = new MDnsDiscovery(node, "_ipfs-discovery._udp.local.", 60, null); + this.mdns.add(mdns); + mdns.addHandler(peerInfo -> { + PeerId remote = PeerId.fromBase58(peerInfo.getPeerId().toBase58().substring(1)); // Not sure what's wrong with peerInfo, but this works + if (!remote.equals(node.getPeerId())) { + LOG.info(node.getPeerId() + " found local peer: " + remote.toBase58() + ", addrs: " + peerInfo.getAddresses()); + Multiaddr[] remoteAddrs = peerInfo.getAddresses().toArray(new Multiaddr[0]); + KademliaController ctr = dht.dial(node, remote, remoteAddrs).getController().join(); + ctr.closerPeers(node.getPeerId().getBytes()).join(); + node.getAddressBook().addAddrs(remote, 0, remoteAddrs); + } + return null; + }); + mdns.start(); + + blockProvider.ifPresent(PeriodicBlockProvider::start); } public CompletableFuture stop() throws Exception { if (records != null) { records.close(); } - blockProvider.ifPresent(b -> b.stop()); + blockProvider.ifPresent(PeriodicBlockProvider::stop); dht.stopBootstrapThread(); + for (MDnsDiscovery m : mdns) { + m.stop(); + } return node != null ? node.stop() : CompletableFuture.completedFuture(null); } @@ -179,6 +229,7 @@ public static BlockMetadataStore buildBlockMetadata(Args a) { throw new RuntimeException(e); } } + public static Blockstore buildBlockStore(Config config, Path ipfsPath, BlockMetadataStore meta, boolean updateMetadb) { Blockstore withMetadb; if (config.datastore.blockMount.prefix.equals("flatfs.datastore")) { @@ -206,9 +257,9 @@ public static Blockstore typeLimited(Blockstore blocks, Config config) { public static Blockstore filteredBlockStore(Blockstore blocks, Config config) { if (config.datastore.filter.type == FilterType.BLOOM) { return FilteredBlockstore.bloomBased(blocks, config.datastore.filter.falsePositiveRate); - } else if(config.datastore.filter.type == FilterType.INFINI) { + } else if (config.datastore.filter.type == FilterType.INFINI) { return FilteredBlockstore.infiniBased(blocks, config.datastore.filter.falsePositiveRate); - } else if(config.datastore.filter.type == FilterType.NONE) { + } else if (config.datastore.filter.type == FilterType.NONE) { return blocks; } else { throw new IllegalStateException("Unhandled filter type: " + config.datastore.filter.type); @@ -224,8 +275,8 @@ public static EmbeddedIpfs build(RecordStore records, BlockRequestAuthoriser authoriser, Optional handler, boolean localEnabled) { - return build(records, blocks, provideBlocks, swarmAddresses, bootstrap, identity, authoriser, handler, localEnabled, - Optional.empty(), Optional.empty()); + return build(records, blocks, provideBlocks, swarmAddresses, bootstrap, identity, Collections.emptyList(), + authoriser, handler, localEnabled, Optional.empty(), Optional.empty()); } public static EmbeddedIpfs build(RecordStore records, @@ -234,6 +285,7 @@ public static EmbeddedIpfs build(RecordStore records, List swarmAddresses, List bootstrap, IdentitySection identity, + List announce, BlockRequestAuthoriser authoriser, Optional handler, boolean localEnabled, @@ -245,7 +297,7 @@ public static EmbeddedIpfs build(RecordStore records, ProviderStore providers = new RamProviderStore(10_000); HostBuilder builder = new HostBuilder().setIdentity(identity.privKeyProtobuf).listen(swarmAddresses); - if (! builder.getPeerId().equals(identity.peerId)) { + if (!builder.getPeerId().equals(identity.peerId)) { throw new IllegalStateException("PeerId invalid"); } Multihash ourPeerId = Multihash.deserialize(builder.getPeerId().getBytes()); @@ -268,9 +320,9 @@ public static EmbeddedIpfs build(RecordStore records, Host node = builder.addProtocols(protocols).build(); Optional> newBlockProvider = provideBlocks ? - Optional.of(((ProvidingBlockstore)blockstore).toPublish) : + Optional.of(((ProvidingBlockstore) blockstore).toPublish) : Optional.empty(); - return new EmbeddedIpfs(node, blockstore, records, dht, bitswap, httpHandler, bootstrap, newBlockProvider); + return new EmbeddedIpfs(node, blockstore, records, dht, bitswap, httpHandler, bootstrap, newBlockProvider, announce); } public static Multiaddr[] getAddresses(Host node, Kademlia dht, Multihash targetNodeId) throws ConnectionException { diff --git a/src/main/java/org/peergos/HostBuilder.java b/src/main/java/org/peergos/HostBuilder.java index 58621917..7c19121c 100644 --- a/src/main/java/org/peergos/HostBuilder.java +++ b/src/main/java/org/peergos/HostBuilder.java @@ -1,25 +1,40 @@ package org.peergos; -import io.ipfs.multiaddr.*; +import io.ipfs.multiaddr.MultiAddress; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import io.libp2p.core.crypto.*; -import io.libp2p.core.dsl.*; -import io.libp2p.core.multiformats.*; -import io.libp2p.core.multistream.*; -import io.libp2p.core.mux.*; -import io.libp2p.crypto.keys.*; -import io.libp2p.protocol.*; -import io.libp2p.security.noise.*; -import io.libp2p.transport.tcp.*; +import io.libp2p.core.ConnectionHandler; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.StreamPromise; import io.libp2p.core.crypto.KeyKt; -import org.peergos.blockstore.*; -import org.peergos.protocol.autonat.*; -import org.peergos.protocol.bitswap.*; -import org.peergos.protocol.circuit.*; -import org.peergos.protocol.dht.*; -import java.util.*; -import java.util.stream.*; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.dsl.Builder; +import io.libp2p.core.dsl.BuilderJKt; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.core.multistream.ProtocolBinding; +import io.libp2p.core.mux.StreamMuxerProtocol; +import io.libp2p.crypto.keys.Ed25519Kt; +import io.libp2p.protocol.IdentifyBinding; +import io.libp2p.protocol.IdentifyController; +import io.libp2p.protocol.IdentifyProtocol; +import io.libp2p.protocol.Ping; +import io.libp2p.security.noise.NoiseXXSecureChannel; +import io.libp2p.transport.tcp.TcpTransport; +import org.peergos.blockstore.Blockstore; +import org.peergos.protocol.autonat.AutonatProtocol; +import org.peergos.protocol.bitswap.Bitswap; +import org.peergos.protocol.bitswap.BitswapEngine; +import org.peergos.protocol.circuit.CircuitHopProtocol; +import org.peergos.protocol.circuit.CircuitStopProtocol; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.KademliaEngine; +import org.peergos.protocol.dht.ProviderStore; +import org.peergos.protocol.dht.RecordStore; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; public class HostBuilder { private PrivKey privKey; @@ -46,21 +61,21 @@ public List getProtocols() { public Optional getWanDht() { return protocols.stream() .filter(p -> p instanceof Kademlia && p.getProtocolDescriptor().getAnnounceProtocols().contains("/ipfs/kad/1.0.0")) - .map(p -> (Kademlia)p) + .map(p -> (Kademlia) p) .findFirst(); } public Optional getBitswap() { return protocols.stream() .filter(p -> p instanceof Bitswap) - .map(p -> (Bitswap)p) + .map(p -> (Bitswap) p) .findFirst(); } public Optional getRelayHop() { return protocols.stream() .filter(p -> p instanceof CircuitHopProtocol.Binding) - .map(p -> (CircuitHopProtocol.Binding)p) + .map(p -> (CircuitHopProtocol.Binding) p) .findFirst(); } @@ -93,7 +108,6 @@ public HostBuilder setIdentity(byte[] privKey) { } - public HostBuilder setPrivKey(PrivKey privKey) { this.privKey = privKey; this.peerId = PeerId.fromPubKey(privKey.publicKey()); @@ -182,7 +196,7 @@ public static Host build(PrivKey privKey, if (connection.isInitiator()) return; addrs.getAddrs(remotePeer).thenAccept(existing -> { - if (! existing.isEmpty()) + if (!existing.isEmpty()) return; StreamPromise stream = connection.muxerSession() .createStream(new IdentifyBinding(new IdentifyProtocol())); @@ -217,7 +231,7 @@ public static Host build(PrivKey privKey, }); for (ProtocolBinding protocol : protocols) { if (protocol instanceof HostConsumer) - ((HostConsumer)protocol).setHost(host); + ((HostConsumer) protocol).setHost(host); } return host; } diff --git a/src/main/java/org/peergos/IpnsPublisher.java b/src/main/java/org/peergos/IpnsPublisher.java index 77f5b3d7..0f9aeacd 100644 --- a/src/main/java/org/peergos/IpnsPublisher.java +++ b/src/main/java/org/peergos/IpnsPublisher.java @@ -1,27 +1,39 @@ package org.peergos; -import io.ipfs.multiaddr.*; -import io.ipfs.multihash.*; -import io.libp2p.core.*; -import io.libp2p.core.crypto.*; -import io.libp2p.crypto.keys.*; -import org.peergos.blockstore.*; -import org.peergos.config.*; -import org.peergos.protocol.dht.*; -import org.peergos.protocol.ipns.*; -import org.peergos.util.*; +import io.ipfs.multiaddr.MultiAddress; +import io.ipfs.multihash.Multihash; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.KeyKt; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.crypto.keys.Ed25519Kt; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.config.Config; +import org.peergos.config.IdentitySection; +import org.peergos.protocol.dht.RamRecordStore; +import org.peergos.protocol.ipns.IPNS; +import org.peergos.protocol.ipns.IpnsRecord; +import org.peergos.util.ArrayOps; -import java.io.*; -import java.nio.file.*; -import java.time.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.stream.*; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; public class IpnsPublisher { private static final ExecutorService ioExec = Executors.newFixedThreadPool(50); + public static void main(String[] a) throws Exception { Path publishFile = Paths.get("publishers.txt"); EmbeddedIpfs publisher = startIpfs(); @@ -36,7 +48,7 @@ public static void main(String[] a) throws Exception { .collect(Collectors.toList()); System.out.println("Resolving " + records.size() + " keys"); - for (int c=0; c < 100; c++) { + for (int c = 0; c < 100; c++) { long t0 = System.currentTimeMillis(); List recordCounts = resolveAndRepublish(records, resolver, publisher); Path resultsFile = Paths.get("publish-resolve-counts-" + LocalDateTime.now().withNano(0) + ".txt"); @@ -75,7 +87,7 @@ public static void main(String[] a) throws Exception { System.out.println("published " + done + " / " + keycount); } long t1 = System.currentTimeMillis(); - System.out.println("Published all in " + (t1-t0)/1000 + "s"); + System.out.println("Published all in " + (t1 - t0) / 1000 + "s"); publisher.stop().join(); } System.exit(0); diff --git a/src/main/java/org/peergos/PeerAddresses.java b/src/main/java/org/peergos/PeerAddresses.java index f2afb379..c2efe8c8 100644 --- a/src/main/java/org/peergos/PeerAddresses.java +++ b/src/main/java/org/peergos/PeerAddresses.java @@ -15,6 +15,7 @@ import java.util.*; import java.util.function.*; import java.util.stream.*; +import java.util.stream.Stream; public class PeerAddresses { public final Multihash peerId; @@ -57,7 +58,13 @@ public static PeerAddresses fromProtobuf(Dht.Message.Peer peer) { Multihash peerId = Multihash.deserialize(peer.getId().toByteArray()); List addrs = peer.getAddrsList() .stream() - .map(b -> Multiaddr.deserialize(b.toByteArray())) + .flatMap(b -> { + try { + return Stream.of(Multiaddr.deserialize(b.toByteArray())); + } catch (Exception e) { + return Stream.empty(); + } + }) .collect(Collectors.toList()); return new PeerAddresses(peerId, addrs); } diff --git a/src/main/java/org/peergos/RamAddressBook.java b/src/main/java/org/peergos/RamAddressBook.java index b871d27b..8de76b9f 100644 --- a/src/main/java/org/peergos/RamAddressBook.java +++ b/src/main/java/org/peergos/RamAddressBook.java @@ -1,19 +1,28 @@ package org.peergos; -import io.libp2p.core.*; -import io.libp2p.core.multiformats.*; -import org.jetbrains.annotations.*; -import org.peergos.util.*; - -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.*; +import io.libp2p.core.AddressBook; +import io.libp2p.core.PeerId; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.core.multiformats.MultiaddrComponent; +import io.libp2p.core.multiformats.Protocol; +import org.jetbrains.annotations.NotNull; +import org.peergos.util.LRUCache; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import java.util.stream.Stream; public class RamAddressBook implements AddressBook { - // TODO: Does this need to be public? - public Map> addresses; + private final Map> addresses; public RamAddressBook() { addresses = Collections.synchronizedMap(new LRUCache<>(10_000)); diff --git a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java index 0d6327be..4ac10e83 100644 --- a/src/main/java/org/peergos/blockstore/CidInfiniFilter.java +++ b/src/main/java/org/peergos/blockstore/CidInfiniFilter.java @@ -35,13 +35,18 @@ public static CidInfiniFilter build(Blockstore bs) { public static CidInfiniFilter build(Blockstore bs, double falsePositiveRate) { List refs = bs.refs(false).join(); int nBlocks = refs.size()*5/4; // increase by 25% to avoid expansion during build + CidInfiniFilter infini = build(nBlocks, falsePositiveRate); + refs.forEach(c -> infini.add(c)); + return infini; + } + + public static CidInfiniFilter build(int nBlocks, double falsePositiveRate) { int nextPowerOfTwo = Math.max(17, (int) (1 + Math.log(nBlocks) / Math.log(2))); double expansionAlpha = 0.8; int bitsPerEntry = (int)(4 - Math.log(falsePositiveRate / expansionAlpha) / Math.log(2) + 1); LOG.info("Using infini filter of initial size " + ((double)(bitsPerEntry * (1 << nextPowerOfTwo) / 8) / 1024 / 1024) + " MiB"); ChainedInfiniFilter infini = new ChainedInfiniFilter(nextPowerOfTwo, bitsPerEntry); infini.set_expand_autonomously(true); - refs.forEach(c -> infini.insert(c.toBytes(), true)); return new CidInfiniFilter(infini); } } diff --git a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java index ab58a2bd..e2ef0adc 100644 --- a/src/main/java/org/peergos/blockstore/FilteredBlockstore.java +++ b/src/main/java/org/peergos/blockstore/FilteredBlockstore.java @@ -8,27 +8,48 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.*; public class FilteredBlockstore implements Blockstore { private final Blockstore blocks; - private final Filter filter; + private final Filter present; + private volatile Filter absent; + private final AtomicLong absentCount = new AtomicLong(0); - public FilteredBlockstore(Blockstore blocks, Filter filter) { + public FilteredBlockstore(Blockstore blocks, Filter present) { this.blocks = blocks; - this.filter = filter; + this.present = present; + this.absent = buildAbsentFilter(); } public CompletableFuture bloomAdd(Cid cid) { - filter.add(cid); + present.add(cid); return CompletableFuture.completedFuture(true); } + private static Filter buildAbsentFilter() { + return CidInfiniFilter.build(1_000, 0.001); + } + + private void addAbsentBlock(Cid c) { + if (absentCount.get() > 10_000) { + absentCount.set(0); + absent = buildAbsentFilter(); + } + absentCount.incrementAndGet(); + absent.add(c); + } + @Override public CompletableFuture has(Cid c) { - if (filter.has(c)) - return blocks.has(c); + if (present.has(c) && ! absent.has(c)) + return blocks.has(c).thenApply(res -> { + if (! res) + addAbsentBlock(c); + return res; + }); return CompletableFuture.completedFuture(false); } @@ -40,15 +61,19 @@ public CompletableFuture hasAny(Multihash h) { @Override public CompletableFuture> get(Cid c) { - if (filter.has(c)) - return blocks.get(c); + if (present.has(c) && ! absent.has(c)) { + return blocks.get(c).exceptionally(t -> { + addAbsentBlock(c); + return Optional.empty(); + }); + } return CompletableFuture.completedFuture(Optional.empty()); } @Override public CompletableFuture put(byte[] block, Cid.Codec codec) { return blocks.put(block, codec) - .thenApply(filter::add); + .thenApply(present::add); } @Override diff --git a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java index aff3b92d..e80a746a 100644 --- a/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java +++ b/src/main/java/org/peergos/blockstore/s3/S3Blockstore.java @@ -10,16 +10,27 @@ import org.peergos.blockstore.metadatadb.BlockMetadata; import org.peergos.blockstore.metadatadb.BlockMetadataStore; import org.peergos.cbor.CborObject; +import org.peergos.util.ArrayOps; +import org.peergos.util.Futures; import org.peergos.util.Hasher; -import org.peergos.util.*; +import org.peergos.util.HttpUtil; +import org.peergos.util.Logging; +import org.peergos.util.Pair; import javax.net.ssl.SSLException; -import java.io.*; +import java.io.FileNotFoundException; +import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.file.Files; import java.nio.file.Path; import java.time.ZonedDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; @@ -28,7 +39,9 @@ import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; public class S3Blockstore implements Blockstore { @@ -130,13 +143,16 @@ public S3Blockstore(Map params, BlockMetadataStore blockMetadata hasher = new Hasher(); LOG.info("Using S3BlockStore"); } + private String getHost() { return bucket + "." + regionEndpoint; } + private boolean useHttps() { String host = getHost(); - return ! host.endsWith("localhost") && ! host.contains("localhost:"); + return !host.endsWith("localhost") && !host.contains("localhost:"); } + private Map readKeysFromAwsConfig() { String filePath = System.getenv("HOME") + "/" + ".aws/credentials"; LOG.info("Reading [default] config from: " + filePath); @@ -146,19 +162,19 @@ private Map readKeysFromAwsConfig() { boolean foundDefaultSection = false; boolean foundAccessKey = false; boolean foundSecretKey = false; - for(String line : lines) { + for (String line : lines) { String trimmedLine = line.trim(); - if(!foundDefaultSection) { - if (trimmedLine.equals("[default]")){ + if (!foundDefaultSection) { + if (trimmedLine.equals("[default]")) { foundDefaultSection = true; } } else { if (trimmedLine.startsWith("[")) { throw new IllegalStateException("Unable to read expected fields in: " + filePath); - } else if(trimmedLine.startsWith("aws_access_key_id")) { + } else if (trimmedLine.startsWith("aws_access_key_id")) { params.put("aws_access_key_id", extractParamValue(trimmedLine)); foundAccessKey = true; - } else if(trimmedLine.startsWith("aws_secret_access_key")) { + } else if (trimmedLine.startsWith("aws_secret_access_key")) { params.put("aws_secret_access_key", extractParamValue(trimmedLine)); foundSecretKey = true; } @@ -175,10 +191,11 @@ private Map readKeysFromAwsConfig() { throw new IllegalStateException("Unable to find aws_secret_access_key"); } } catch (IOException ioe) { - throw new IllegalStateException("Unable to read: " + filePath, ioe); + throw new IllegalStateException("Unable to read: " + filePath, ioe); } return params; } + private String extractParamValue(String line) { int equalsIndex = line.indexOf("="); if (equalsIndex == -1) { @@ -186,16 +203,18 @@ private String extractParamValue(String line) { } return line.substring(equalsIndex + 1).trim(); } + private String getParam(Map params, String key) { if (params.containsKey(key)) { - return ((String )params.get(key)).trim(); + return ((String) params.get(key)).trim(); } else { throw new IllegalStateException("Expecting param: " + key); } } + private String getParam(Map params, String key, String defaultValue) { if (params.containsKey(key)) { - return ((String )params.get(key)).trim(); + return ((String) params.get(key)).trim(); } else { return defaultValue; } @@ -233,7 +252,7 @@ public void updateMetadataStoreIfEmpty() { ForkJoinPool pool = new ForkJoinPool(updateParallelism); int batchSize = all.size() / updateParallelism; AtomicLong progress = new AtomicLong(0); - int tenth = batchSize/10; + int tenth = batchSize / 10; List>> futures = IntStream.range(0, updateParallelism) .mapToObj(b -> pool.submit(() -> IntStream.range(b * batchSize, (b + 1) * batchSize) @@ -256,13 +275,14 @@ public void updateMetadataStoreIfEmpty() { private static V getWithBackoff(Supplier req) { long sleep = 100; - for (int i=0; i < 20; i++) { + for (int i = 0; i < 20; i++) { try { return req.get(); } catch (RateLimitException e) { try { Thread.sleep(sleep); - } catch (InterruptedException f) {} + } catch (InterruptedException f) { + } sleep *= 2; } } @@ -294,7 +314,7 @@ private CompletableFuture> getSizeWithoutRetry(Cid cid) { Map> headRes = HttpUtil.head(headUrl.base, headUrl.fields); blockHeads.inc(); long size = Long.parseLong(headRes.get("Content-Length").get(0)); - return Futures.of(Optional.of((int)size)); + return Futures.of(Optional.of((int) size)); } catch (FileNotFoundException f) { LOG.warning("S3 404 error reading " + cid); return Futures.of(Optional.empty()); @@ -306,7 +326,7 @@ private CompletableFuture> getSizeWithoutRetry(Cid cid) { throw new RateLimitException(); } boolean notFound = msg.startsWith("NoSuchKey"); - if (! notFound) { + if (!notFound) { LOG.warning("S3 error reading " + cid); LOG.log(Level.WARNING, msg, e); } @@ -344,12 +364,12 @@ private CompletableFuture> getWithoutRetry(Cid cid) { throw new RateLimitException(); } boolean notFound = msg.startsWith("NoSuchKey"); - if (! notFound) { + if (!notFound) { LOG.warning("S3 error reading " + path); LOG.log(Level.WARNING, msg, e); } failedBlockGets.inc(); - throw new RuntimeException(e.getMessage(), e); + return Futures.errored(e); } finally { readTimer.observeDuration(); } @@ -359,6 +379,7 @@ private CompletableFuture> getWithoutRetry(Cid cid) { public CompletableFuture put(byte[] block, Cid.Codec codec) { return getWithBackoff(() -> putWithoutRetry(block, codec)); } + public CompletableFuture putWithoutRetry(byte[] block, Cid.Codec codec) { Histogram.Timer writeTimer = writeTimerLog.labels("write").startTimer(); byte[] hash = Hash.sha256(block); @@ -368,7 +389,7 @@ public CompletableFuture putWithoutRetry(byte[] block, Cid.Codec codec) { String s3Key = folder + key; Map extraHeaders = new TreeMap<>(); extraHeaders.put("Content-Type", "application/octet-stream"); - String contentHash = ArrayOps.bytesToHex(hash); + String contentHash = ArrayOps.bytesToHex(hash); PresignedUrl putUrl = S3Request.preSignPut(s3Key, block.length, contentHash, false, S3AdminRequests.asAwsDate(ZonedDateTime.now()), host, extraHeaders, region, accessKeyId, secretKey, useHttps, hasher).join(); HttpUtil.put(putUrl.base, putUrl.fields, block); diff --git a/src/main/java/org/peergos/net/APIHandler.java b/src/main/java/org/peergos/net/APIHandler.java index ad49f8f3..d4f225dd 100755 --- a/src/main/java/org/peergos/net/APIHandler.java +++ b/src/main/java/org/peergos/net/APIHandler.java @@ -1,25 +1,39 @@ package org.peergos.net; +import com.sun.net.httpserver.HttpExchange; import io.ipfs.cid.Cid; -import io.ipfs.multihash.*; +import io.ipfs.multihash.Multihash; import io.libp2p.core.PeerId; -import io.libp2p.crypto.keys.*; -import org.peergos.*; -import org.peergos.cbor.*; -import org.peergos.protocol.ipns.*; -import org.peergos.protocol.ipns.pb.*; -import org.peergos.util.*; -import com.sun.net.httpserver.HttpExchange; +import org.peergos.AggregatedMetrics; +import org.peergos.EmbeddedIpfs; +import org.peergos.HashedBlock; +import org.peergos.PeerAddresses; +import org.peergos.Want; +import org.peergos.cbor.CborObject; +import org.peergos.protocol.ipns.IpnsRecord; +import org.peergos.protocol.ipns.pb.Ipns; +import org.peergos.util.ArrayOps; +import org.peergos.util.HttpUtil; +import org.peergos.util.JSONParser; +import org.peergos.util.Logging; +import org.peergos.util.Version; -import java.io.*; -import java.nio.*; -import java.util.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.logging.Logger; -import java.util.stream.*; +import java.util.stream.Collectors; public class APIHandler extends Handler { public static final String API_URL = "/api/v0/"; - public static final Version CURRENT_VERSION = Version.parse("0.7.9"); + public static final Version CURRENT_VERSION = Version.parse("0.8.0"); private static final Logger LOG = Logging.LOG(); private static final boolean LOGGING = true; @@ -52,7 +66,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { long t1 = System.currentTimeMillis(); String path = httpExchange.getRequestURI().getPath(); try { - if (! path.startsWith(API_URL)) + if (!path.startsWith(API_URL)) throw new IllegalStateException("Unsupported api version, required: " + API_URL); path = path.substring(API_URL.length()); // N.B. URI.getQuery() decodes the query string @@ -64,7 +78,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { AggregatedMetrics.API_ID.inc(); PeerId peerId = ipfs.node.getPeerId(); Map res = new HashMap<>(); - res.put("ID", peerId.toBase58()); + res.put("ID", peerId.toBase58()); replyJson(httpExchange, JSONParser.toString(res)); break; } @@ -90,7 +104,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { .map(a -> Boolean.parseBoolean(a.get(0))) .orElse(true); List block = ipfs.getBlocks(List.of(new Want(Cid.decode(args.get(0)), auth)), peers, addToBlockstore); - if (! block.isEmpty()) { + if (!block.isEmpty()) { replyBytes(httpExchange, block.get(0).block); } else { try { @@ -104,7 +118,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { case BULK_STAT: { AggregatedMetrics.API_BLOCK_STAT_BULK.inc(); Map json = (Map) JSONParser.parse(new String(readFully(httpExchange.getRequestBody()))); - List wants = ((List>)json.get("wants")) + List wants = ((List>) json.get("wants")) .stream() .map(Want::fromJson) .collect(Collectors.toList()); @@ -135,7 +149,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { case PUT: { // https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-block-put AggregatedMetrics.API_BLOCK_PUT.inc(); List format = params.get("format"); - Optional formatOpt = format !=null && format.size() == 1 ? Optional.of(format.get(0)) : Optional.empty(); + Optional formatOpt = format != null && format.size() == 1 ? Optional.of(format.get(0)) : Optional.empty(); if (formatOpt.isEmpty()) { throw new APIException("argument \"format\" is required"); } @@ -184,7 +198,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { case RM_BULK: { AggregatedMetrics.API_BLOCK_RM_BULK.inc(); Map json = (Map) JSONParser.parse(new String(readFully(httpExchange.getRequestBody()))); - List cids = ((List)json.get("cids")) + List cids = ((List) json.get("cids")) .stream() .map(Cid::decode) .collect(Collectors.toList()); @@ -213,7 +227,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { } Optional auth = Optional.ofNullable(params.get("auth")).map(a -> a.get(0)); List block = ipfs.getBlocks(List.of(new Want(Cid.decode(args.get(0)), auth)), Collections.emptySet(), false); - if (! block.isEmpty()) { + if (!block.isEmpty()) { Map res = new HashMap<>(); res.put("Size", block.get(0).block.length); replyJson(httpExchange, JSONParser.toString(res)); @@ -296,7 +310,7 @@ public void handleCallToAPI(HttpExchange httpExchange) { throw new IllegalStateException("Couldn't resolve " + signer); IpnsRecord latest = records.get(records.size() - 1); Ipns.IpnsEntry entry = Ipns.IpnsEntry.parseFrom(ByteBuffer.wrap(latest.raw)); - Map res = new HashMap<>(); + Map res = new HashMap<>(); res.put("sig", ArrayOps.bytesToHex(entry.getSignatureV2().toByteArray())); res.put("data", ArrayOps.bytesToHex(entry.getData().toByteArray())); String json = JSONParser.toString(res); @@ -319,10 +333,10 @@ public void handleCallToAPI(HttpExchange httpExchange) { } private byte[] readFully(InputStream in) throws IOException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - byte[] b = new byte[0x1000]; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] b = new byte[0x1000]; int nRead; - while ((nRead = in.read(b, 0, b.length)) != -1 ) + while ((nRead = in.read(b, 0, b.length)) != -1) bout.write(b, 0, nRead); in.close(); return bout.toByteArray(); diff --git a/src/main/java/org/peergos/protocol/IdentifyBuilder.java b/src/main/java/org/peergos/protocol/IdentifyBuilder.java index 402120c6..78a887f9 100644 --- a/src/main/java/org/peergos/protocol/IdentifyBuilder.java +++ b/src/main/java/org/peergos/protocol/IdentifyBuilder.java @@ -15,12 +15,12 @@ public class IdentifyBuilder { - public static void addIdentifyProtocol(Host node) { + public static void addIdentifyProtocol(Host node, List announceAddresses) { IdentifyOuterClass.Identify.Builder identifyBuilder = IdentifyOuterClass.Identify.newBuilder() .setProtocolVersion("ipfs/0.1.0") .setAgentVersion("nabu/v0.1.0") .setPublicKey(ByteArrayExtKt.toProtobuf(node.getPrivKey().publicKey().bytes())) - .addAllListenAddrs(node.listenAddresses().stream() + .addAllListenAddrs(Stream.concat(node.listenAddresses().stream(), announceAddresses.stream()) .flatMap(a -> expandWildcardAddresses(a).stream()) .map(Multiaddr::serialize) .map(ByteArrayExtKt::toProtobuf) diff --git a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java index 3e9927c8..fb4ddb9c 100644 --- a/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java +++ b/src/main/java/org/peergos/protocol/bitswap/BitswapEngine.java @@ -158,7 +158,12 @@ public void receiveMessage(MessageOuterClass.Message msg, Stream source, Counter int presentBlocks = 0; if (msg.hasWantlist()) { for (MessageOuterClass.Message.Wantlist.Entry e : msg.getWantlist().getEntriesList()) { - Cid c = Cid.cast(e.getBlock().toByteArray()); + Cid c; + try { + c = Cid.cast(e.getBlock().toByteArray()); + } catch (Exception ex) { + continue; + } Optional auth = e.getAuth().isEmpty() ? Optional.empty() : Optional.of(ArrayOps.bytesToHex(e.getAuth().toByteArray())); boolean isCancel = e.getCancel(); boolean sendDontHave = e.getSendDontHave(); diff --git a/src/main/java/org/peergos/protocol/dht/Kademlia.java b/src/main/java/org/peergos/protocol/dht/Kademlia.java index 0e3eb9b7..89b8b039 100644 --- a/src/main/java/org/peergos/protocol/dht/Kademlia.java +++ b/src/main/java/org/peergos/protocol/dht/Kademlia.java @@ -1,27 +1,59 @@ package org.peergos.protocol.dht; -import com.offbynull.kademlia.*; -import io.ipfs.multiaddr.*; +import com.offbynull.kademlia.Id; +import io.ipfs.multiaddr.MultiAddress; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import io.libp2p.core.crypto.*; -import io.libp2p.core.multiformats.*; +import io.libp2p.core.AddressBook; +import io.libp2p.core.Connection; +import io.libp2p.core.ConnectionClosedException; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.StreamPromise; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.multiformats.Multiaddr; import io.libp2p.core.multiformats.Protocol; -import io.libp2p.core.multistream.*; -import io.libp2p.etc.types.*; -import io.libp2p.protocol.*; -import org.peergos.*; -import org.peergos.protocol.dnsaddr.*; -import org.peergos.protocol.ipns.*; +import io.libp2p.core.multistream.StrictProtocolBinding; +import io.libp2p.etc.types.NonCompleteException; +import io.libp2p.etc.types.NothingToCompleteException; +import io.libp2p.protocol.Identify; +import io.libp2p.protocol.IdentifyController; +import org.peergos.AddressBookConsumer; +import org.peergos.ClientMode; +import org.peergos.Hash; +import org.peergos.PeerAddresses; +import org.peergos.Providers; +import org.peergos.protocol.dnsaddr.DnsAddr; +import org.peergos.protocol.ipns.GetResult; +import org.peergos.protocol.ipns.IPNS; +import org.peergos.protocol.ipns.IpnsMapping; +import org.peergos.protocol.ipns.IpnsRecord; import org.peergos.util.Logging; -import java.time.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import java.util.logging.*; -import java.util.stream.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; import java.util.stream.Stream; public class Kademlia extends StrictProtocolBinding implements AddressBookConsumer, ClientMode { @@ -82,12 +114,14 @@ public int bootstrapRoutingTable(Host host, List addrs, Predicate< try { future.get(5, TimeUnit.SECONDS); successes++; - } catch (Exception e) {} + } catch (Exception e) { + } } return successes; } private AtomicBoolean running = new AtomicBoolean(false); + public void startBootstrapThread(Host us) { running.set(true); new Thread(() -> { @@ -106,9 +140,15 @@ public void stopBootstrapThread() { running.set(false); } - public boolean connectTo(Host us, PeerAddresses peer) { + private boolean connectTo(Host us, PeerAddresses peer) { try { - new Identify().dial(us, PeerId.fromBase58(peer.peerId.toBase58()), getPublic(peer)).getController().join().id().join(); + PeerId ourPeerId = PeerId.fromBase58(peer.peerId.toBase58()); + StreamPromise conn = new Identify().dial(us, ourPeerId, getPublic(peer)); + try { + conn.getController().join().id().join(); + } finally { + conn.getStream().thenApply(s -> s.close()); + } return true; } catch (Exception e) { if (e.getCause() instanceof NothingToCompleteException || e.getCause() instanceof NonCompleteException) @@ -129,12 +169,19 @@ public void bootstrap(Host us) { // lookup our own peer id to keep our nearest neighbours up-to-date, // and connect to all of them, so they know about our addresses List closestToUs = findClosestPeers(Multihash.deserialize(us.getPeerId().getBytes()), replication, us); - int connectedClosest = 0; + List connected = new ArrayList<>(); for (PeerAddresses peer : closestToUs) { if (connectTo(us, peer)) - connectedClosest++; + connected.add(peer.peerId); + } + LOG.info("Bootstrap connected to " + connected.size() + " nodes close to us. " + connected.stream().map(Multihash::toString).sorted().limit(5).collect(Collectors.toList())); + + List allConns = us.getNetwork().getConnections(); + Set activeConns = us.getStreams().stream().map(s -> s.getConnection()).collect(Collectors.toSet()); + List toClose = allConns.stream().filter(c -> !activeConns.contains(c)).collect(Collectors.toList()); + for (Connection conn : toClose) { + conn.close(); } - LOG.fine("Bootstrap connected to " + connectedClosest + " nodes close to us."); } @Override @@ -142,6 +189,19 @@ public boolean isClient() { return this.clientMode; } + private static CompletableFuture closeAfter(CompletableFuture sf, Supplier> query) { + CompletableFuture res = new CompletableFuture<>(); + query.get().thenAccept(v -> { + sf.thenAccept(s -> s.close()); + res.complete(v); + }).exceptionally(t -> { + sf.thenAccept(s -> s.close()); + res.completeExceptionally(t); + return null; + }); + return res; + } + static class RoutingEntry { public final Id key; public final PeerAddresses addresses; @@ -170,6 +230,7 @@ public List findClosestPeers(Multihash peerIdkey, int maxCount, H byte[] key = peerIdkey.toBytes(); return findClosestPeers(key, maxCount, us); } + public List findClosestPeers(byte[] key, int maxCount, Host us) { Id keyId = Id.create(Hash.sha256(key), 256); SortedSet closest = Collections.synchronizedSortedSet(new TreeSet<>((a, b) -> compareKeys(a, b, keyId))); @@ -191,6 +252,8 @@ public List findClosestPeers(byte[] key, int maxCount, Host us) { Set queried = Collections.synchronizedSet(new HashSet<>()); int queryParallelism = alpha; + if (toQuery.isEmpty()) + LOG.info("Couldn't find any local peers in kademlia routing table"); while (true) { // The set of next query candidates. Pick as many peers from the candidate peers (closest) as the alpha concurrency factor allows. List thisRound = toQuery.stream() @@ -226,12 +289,12 @@ public List findClosestPeers(byte[] key, int maxCount, Host us) { } } } catch (Exception e) { - // couldn't contact peer + // couldn't contact peer } } // if no new peers in top k were returned we are done - if (! foundCloser) + if (!foundCloser) break; } return closest.stream() @@ -261,12 +324,16 @@ public CompletableFuture> findProviders(Multihash block, Hos List> futures = queryThisRound.stream() .parallel() .map(r -> { - KademliaController res = null; + StreamPromise conn = null; try { - res = dialPeer(r.addresses, us).join(); - return res.getProviders(block).orTimeout(2, TimeUnit.SECONDS); - }catch (Exception e) { + conn = dialPeer(r.addresses, us); + return conn.getController().join() + .getProviders(block).orTimeout(2, TimeUnit.SECONDS); + } catch (Exception e) { return null; + } finally { + if (conn != null) + conn.getStream().thenApply(s -> s.close()); } }).filter(prov -> prov != null) .collect(Collectors.toList()); @@ -284,12 +351,12 @@ public CompletableFuture> findProviders(Multihash block, Hos } } } catch (Exception e) { - if (! (e.getCause() instanceof TimeoutException)) - LOG.fine( "Timeout Exception: " + e.getMessage()); + if (!(e.getCause() instanceof TimeoutException)) + LOG.fine("Timeout Exception: " + e.getMessage()); } } // if no new peers in top k were returned we are done - if (! foundCloser) + if (!foundCloser) break; } @@ -298,17 +365,19 @@ public CompletableFuture> findProviders(Multihash block, Hos private CompletableFuture> getCloserPeers(byte[] key, PeerAddresses target, Host us) { try { - return dialPeer(target, us).orTimeout(2, TimeUnit.SECONDS).join().closerPeers(key); + StreamPromise conn = dialPeer(target, us); + KademliaController contr = conn.getController().orTimeout(2, TimeUnit.SECONDS).join(); + return closeAfter(conn.getStream(), () -> contr.closerPeers(key)); } catch (Exception e) { // we can't dial quic only nodes until it's implemented if (target.addresses.stream().allMatch(a -> a.toString().contains("quic"))) return CompletableFuture.completedFuture(Collections.emptyList()); if (e.getCause() instanceof NothingToCompleteException || e.getCause() instanceof NonCompleteException) { LOG.fine("Couldn't dial " + target.peerId + " addrs: " + target.addresses); - } else if (e.getCause() instanceof TimeoutException) + } else if (e.getCause() instanceof TimeoutException) LOG.fine("Timeout dialing " + target.peerId + " addrs: " + target.addresses); - else if (e.getCause() instanceof ConnectionClosedException) {} - else + else if (e.getCause() instanceof ConnectionClosedException) { + } else LOG.fine("Unknown error: " + e.getMessage()); } return CompletableFuture.completedFuture(Collections.emptyList()); @@ -320,26 +389,34 @@ private Multiaddr[] getPublic(PeerAddresses target) { .collect(Collectors.toList()).toArray(new Multiaddr[0]); } - private CompletableFuture dialPeer(PeerAddresses target, Host us) { + private StreamPromise dialPeer(PeerAddresses target, Host us) { Multiaddr[] multiaddrs = target.addresses.stream() .map(a -> Multiaddr.fromString(a.toString())) - .filter(a -> ! a.has(Protocol.DNS) && ! a.has(Protocol.DNS4) && ! a.has(Protocol.DNS6)) + .filter(a -> !a.has(Protocol.DNS) && !a.has(Protocol.DNS4) && !a.has(Protocol.DNS6)) .collect(Collectors.toList()).toArray(new Multiaddr[0]); - return dial(us, PeerId.fromBase58(target.peerId.toBase58()), multiaddrs).getController(); + return dial(us, PeerId.fromBase58(target.peerId.toBase58()), multiaddrs); } public CompletableFuture provideBlock(Multihash block, Host us, PeerAddresses ourAddrs) { List closestPeers = findClosestPeers(block, replication, us); List> provides = closestPeers.stream() .parallel() - .map(p -> dialPeer(p, us) - .thenCompose(contr -> contr.provide(block, ourAddrs)) - .exceptionally(t -> { - if (t.getCause() instanceof NonCompleteException) + .map(p -> { + StreamPromise conn = dialPeer(p, us); + return conn.getController() + .thenCompose(contr -> contr.provide(block, ourAddrs)) + .thenApply(res -> { + conn.getStream().thenApply(s -> s.close()); + return res; + }) + .exceptionally(t -> { + if (t.getCause() instanceof NonCompleteException) + return true; + LOG.log(Level.FINE, t, t::getMessage); + conn.getStream().thenApply(s -> s.close()); return true; - LOG.log(Level.FINE, t, t::getMessage); - return true; - })) + }); + }) .collect(Collectors.toList()); return CompletableFuture.allOf(provides.toArray(new CompletableFuture[0])); } @@ -362,14 +439,15 @@ private CompletableFuture putValue(Multihash publisher, PeerAddresses peer, Host us) { try { - return dialPeer(peer, us).join() + return dialPeer(peer, us).getController().join() .putValue(publisher, signedRecord); - } catch (Exception e) {} + } catch (Exception e) { + } return CompletableFuture.completedFuture(false); } private boolean hasTransportOverlap(PeerAddresses p) { - return p.addresses.stream().anyMatch(a -> a.has(Protocol.TCP) && ! a.has(Protocol.P2PCIRCUIT)); + return p.addresses.stream().anyMatch(a -> a.has(Protocol.TCP) && !a.has(Protocol.P2PCIRCUIT)); } public CompletableFuture publishValue(Multihash publisher, @@ -397,7 +475,7 @@ public CompletableFuture publishValue(Multihash publisher, .map(p -> new RoutingEntry(Id.create(Hash.sha256(p.peerId.toBytes()), 256), p)) .collect(Collectors.toList())); Set queried = Collections.synchronizedSet(new HashSet<>()); - while (! toQuery.isEmpty()) { + while (!toQuery.isEmpty()) { int remaining = toQuery.size() - 3; List thisRound = toQuery.stream() .filter(r -> hasTransportOverlap(r.addresses)) // don't waste time trying to dial nodes we can't @@ -410,7 +488,7 @@ public CompletableFuture publishValue(Multihash publisher, return CompletableFuture.supplyAsync(() -> getCloserPeers(key, r.addresses, us).thenApply(res -> { List more = new ArrayList<>(); for (PeerAddresses peer : res) { - if (! queried.contains(peer.peerId)) { + if (!queried.contains(peer.peerId)) { Id peerKey = Id.create(Hash.sha256(IPNS.getKey(peer.peerId)), 256); RoutingEntry e = new RoutingEntry(peerKey, peer); more.add(e); @@ -430,7 +508,8 @@ public CompletableFuture publishValue(Multihash publisher, if (publishes.size() >= minPublishes) return; toQuery.addAll(f.orTimeout(2, TimeUnit.SECONDS).join()); - } catch (Exception e) {} + } catch (Exception e) { + } }); // exit early if we have enough results if (publishes.size() >= minPublishes) @@ -440,8 +519,8 @@ public CompletableFuture publishValue(Multihash publisher, System.out.println("Publishing to further nodes, so far only " + publishes.size()); while (publishes.size() < minPublishes && !toQuery.isEmpty()) { List closest = toQuery.stream() - .limit(minPublishes - publishes.size() + 5) - .collect(Collectors.toList()); + .limit(minPublishes - publishes.size() + 5) + .collect(Collectors.toList()); List> lastFutures = closest.stream() .map(r -> { toQuery.remove(r); @@ -456,7 +535,8 @@ public CompletableFuture publishValue(Multihash publisher, lastFutures.forEach(f -> { try { f.orTimeout(2, TimeUnit.SECONDS).join(); - } catch (Exception e) {} + } catch (Exception e) { + } }); } break; @@ -475,7 +555,7 @@ public CompletableFuture resolveIpnsValue(Multihash publisher, Host us, private CompletableFuture> getValueFromPeer(PeerAddresses peer, Multihash publisher, Host us) { try { - return dialPeer(peer, us) + return dialPeer(peer, us).getController() .orTimeout(1, TimeUnit.SECONDS) .join() .getValue(publisher) @@ -485,6 +565,7 @@ private CompletableFuture> getValueFromPeer(PeerAddresses pe return CompletableFuture.completedFuture(Optional.empty()); } } + public List resolveValue(Multihash publisher, int minResults, Host us) { byte[] key = IPNS.getKey(publisher); List candidates = Collections.synchronizedList(new ArrayList<>()); @@ -501,7 +582,7 @@ public List resolveValue(Multihash publisher, int minResults, Host u .collect(Collectors.toList())); Set queried = Collections.synchronizedSet(new HashSet<>()); int countdown = 20; // TODO: Does this have to be 20 as in `replication` or unrelated? - while (! toQuery.isEmpty()) { + while (!toQuery.isEmpty()) { int remaining = toQuery.size() - 3; List thisRound = toQuery.stream() .limit(queryParallelism) @@ -530,7 +611,8 @@ public List resolveValue(Multihash publisher, int minResults, Host u return; f.orTimeout(5, TimeUnit.SECONDS).join() .orTimeout(5, TimeUnit.SECONDS).join(); - } catch (Exception e) {} + } catch (Exception e) { + } }); // exit early if we have enough results if (candidates.size() >= minResults) diff --git a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java index 62fe6c2d..6eb164eb 100644 --- a/src/main/java/org/peergos/protocol/dht/KademliaEngine.java +++ b/src/main/java/org/peergos/protocol/dht/KademliaEngine.java @@ -198,6 +198,7 @@ public void receiveRequest(Dht.Message msg, PeerId source, Stream stream) { case PING: {break;} // Not used any more default: throw new IllegalStateException("Unknown message kademlia type: " + msg.getType()); } + stream.close(); } public static boolean isPublic(Multiaddr addr) { diff --git a/src/main/java/org/peergos/protocol/http/HttpProtocol.java b/src/main/java/org/peergos/protocol/http/HttpProtocol.java index 217c5f43..2d3fd031 100644 --- a/src/main/java/org/peergos/protocol/http/HttpProtocol.java +++ b/src/main/java/org/peergos/protocol/http/HttpProtocol.java @@ -1,8 +1,5 @@ package org.peergos.protocol.http; -import io.ipfs.cid.*; -import io.ipfs.multibase.*; -import io.ipfs.multihash.*; import io.libp2p.core.*; import io.libp2p.core.multistream.*; import io.libp2p.protocol.*; @@ -46,9 +43,11 @@ public Sender(Stream stream) { @Override public void onMessage(@NotNull Stream stream, FullHttpResponse msg) { CompletableFuture req = queue.poll(); - if (req != null) - req.complete(msg.copy()); - msg.release(); + if (req != null) { + req.complete(msg.retain()); + } else { + msg.release(); + } stream.close(); } @@ -86,7 +85,7 @@ public Receiver(HttpRequestProcessor requestHandler) { } private void sendReply(HttpContent reply, Stream p2pstream) { - p2pstream.writeAndFlush(reply.copy()); + p2pstream.writeAndFlush(reply.retain()); } @Override @@ -120,7 +119,14 @@ protected void initChannel(Channel ch) throws Exception { Channel ch = fut.channel(); FullHttpRequest retained = msg.retain(); - fut.addListener(x -> ch.writeAndFlush(retained)); + fut.addListener(x -> { + if (x.isSuccess()) + ch.writeAndFlush(retained).addListener(f -> { + retained.release(); + }); + else + retained.release(); + }); } private static final long TRAFFIC_LIMIT = Long.MAX_VALUE; // This is the total inbound or outbound traffic allowed, not a rate diff --git a/src/main/java/org/peergos/util/Futures.java b/src/main/java/org/peergos/util/Futures.java index 83b55de0..1a264d29 100755 --- a/src/main/java/org/peergos/util/Futures.java +++ b/src/main/java/org/peergos/util/Futures.java @@ -19,4 +19,10 @@ private static T logAndThrow(Throwable t, Optional message) { t.printStackTrace(); throw new RuntimeException(t.getMessage(), t); } + + public static CompletableFuture errored(Throwable t) { + CompletableFuture err = new CompletableFuture<>(); + err.completeExceptionally(t); + return err; + } } diff --git a/src/test/java/org/peergos/APIServiceTest.java b/src/test/java/org/peergos/APIServiceTest.java index 9ecd3db8..8f1763ca 100644 --- a/src/test/java/org/peergos/APIServiceTest.java +++ b/src/test/java/org/peergos/APIServiceTest.java @@ -45,7 +45,7 @@ public void runAPIServiceWithFileStorageTest() { @Test public void bulkGetTest() { EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, - null, null, Optional.empty(), Collections.emptyList(), Optional.empty()); + null, null, Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); Cid cid1 = ipfs.blockstore.put("Hello".getBytes(), Cid.Codec.Raw).join(); Cid cid2= ipfs.blockstore.put("world!".getBytes(), Cid.Codec.Raw).join(); List wants = new ArrayList<>(); @@ -57,7 +57,7 @@ public void bulkGetTest() { public static void runAPIServiceTest(Blockstore blocks) { EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, - null, null, Optional.empty(), Collections.emptyList(), Optional.empty()); + null, null, Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); Cid cid = Cid.decode("zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg"); Assert.assertFalse("cid found", ipfs.blockstore.has(cid).join()); String text = "Hello world!"; diff --git a/src/test/java/org/peergos/BitswapMirrorTest.java b/src/test/java/org/peergos/BitswapMirrorTest.java index c3a7edfb..2cb02869 100644 --- a/src/test/java/org/peergos/BitswapMirrorTest.java +++ b/src/test/java/org/peergos/BitswapMirrorTest.java @@ -23,10 +23,10 @@ public class BitswapMirrorTest { //@Ignore // Local testing only for now - run this prior: ./ipfs pin add zdpuAwfJrGYtiGFDcSV3rDpaUrqCtQZRxMjdC6Eq9PNqLqTGg public void mirrorTree() throws IOException { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true),false, false); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); IPFS kubo = new IPFS("localhost", 5001); Multiaddr kuboAddress = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); preloadBlocksToKubo(kubo); diff --git a/src/test/java/org/peergos/BitswapTest.java b/src/test/java/org/peergos/BitswapTest.java index 25e90b70..faf337a9 100644 --- a/src/test/java/org/peergos/BitswapTest.java +++ b/src/test/java/org/peergos/BitswapTest.java @@ -1,18 +1,27 @@ package org.peergos; -import io.ipfs.cid.*; +import io.ipfs.cid.Cid; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import io.libp2p.core.multiformats.*; -import org.junit.*; -import org.peergos.blockstore.*; -import org.peergos.protocol.bitswap.*; -import org.peergos.protocol.dht.*; +import io.libp2p.core.Host; +import io.libp2p.core.multiformats.Multiaddr; +import org.junit.Test; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.protocol.bitswap.Bitswap; +import org.peergos.protocol.dht.RamProviderStore; +import org.peergos.protocol.dht.RamRecordStore; -import java.nio.charset.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; public class BitswapTest { private static final Random rnd = new Random(28); @@ -20,11 +29,11 @@ public class BitswapTest { @Test public void getBlock() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); RamBlockstore blockstore2 = new RamBlockstore(); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node2 = builder2.build(); node1.start().join(); node2.start().join(); @@ -38,7 +47,7 @@ public void getBlock() { .stream() .map(f -> f.join()) .collect(Collectors.toList()); - if (! Arrays.equals(receivedBlock.get(0).block, blockData)) + if (!Arrays.equals(receivedBlock.get(0).block, blockData)) throw new IllegalStateException("Incorrect block returned!"); } finally { node1.stop(); @@ -49,11 +58,11 @@ public void getBlock() { @Test public void getTenBlocks() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); RamBlockstore blockstore2 = new RamBlockstore(); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node2 = builder2.build(); node1.start().join(); node2.start().join(); @@ -61,8 +70,8 @@ public void getTenBlocks() { Multiaddr address2 = node2.listenAddresses().get(0); List hashes = new ArrayList<>(); Random random = new Random(28); - for (int i=0; i < 10; i++) { - byte[] blockData = new byte[1024*1024]; + for (int i = 0; i < 10; i++) { + byte[] blockData = new byte[1024 * 1024]; random.nextBytes(blockData); Cid hash = blockstore2.put(blockData, Cid.Codec.Raw).join(); hashes.add(hash); @@ -85,7 +94,7 @@ public void getTenBlocks() { @Test public void blockFlooder() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host flooder = builder1.build(); RamBlockstore blockstore2 = new RamBlockstore(); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), @@ -112,7 +121,7 @@ public void blockFlooder() { if (Arrays.equals(receivedBlockAgain.get(0).block, blockData)) throw new IllegalStateException("Received block!"); } catch (CompletionException t) { - if (! (t.getCause() instanceof TimeoutException)) + if (!(t.getCause() instanceof TimeoutException)) throw t; } } finally { diff --git a/src/test/java/org/peergos/BootstrapTest.java b/src/test/java/org/peergos/BootstrapTest.java index 30fb8d15..5940ed97 100644 --- a/src/test/java/org/peergos/BootstrapTest.java +++ b/src/test/java/org/peergos/BootstrapTest.java @@ -1,17 +1,20 @@ package org.peergos; -import io.ipfs.multiaddr.*; +import io.ipfs.multiaddr.MultiAddress; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import org.junit.*; -import org.peergos.blockstore.*; -import org.peergos.protocol.*; -import org.peergos.protocol.dht.*; +import io.libp2p.core.Host; +import org.junit.Test; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.protocol.IdentifyBuilder; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.RamProviderStore; +import org.peergos.protocol.dht.RamRecordStore; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; -import java.util.stream.*; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import java.util.stream.Collectors; public class BootstrapTest { @@ -37,10 +40,10 @@ public class BootstrapTest { @Test public void bootstrap() { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); Multihash node1Id = Multihash.deserialize(node1.getPeerId().getBytes()); try { @@ -53,7 +56,7 @@ public void bootstrap() { // lookup ourselves in DHT to find our nearest nodes List closestPeers = dht.findClosestPeers(node1Id, 20, node1); - if (closestPeers.size() < connections/2) + if (closestPeers.size() < connections / 2) throw new IllegalStateException("Didn't find more close peers after bootstrap: " + closestPeers.size() + " < " + connections); } finally { diff --git a/src/test/java/org/peergos/EmbeddedIpfsTest.java b/src/test/java/org/peergos/EmbeddedIpfsTest.java index 3303aad7..d2baa7aa 100644 --- a/src/test/java/org/peergos/EmbeddedIpfsTest.java +++ b/src/test/java/org/peergos/EmbeddedIpfsTest.java @@ -1,24 +1,42 @@ package org.peergos; -import identify.pb.*; -import io.ipfs.cid.*; -import io.ipfs.multiaddr.*; +import com.sun.net.httpserver.HttpServer; +import identify.pb.IdentifyOuterClass; +import io.ipfs.cid.Cid; +import io.ipfs.multiaddr.MultiAddress; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import io.libp2p.core.crypto.*; -import io.libp2p.core.multiformats.*; -import io.libp2p.crypto.keys.*; -import io.libp2p.protocol.*; -import org.junit.*; -import org.peergos.blockstore.*; -import org.peergos.config.*; -import org.peergos.protocol.dht.*; -import org.peergos.protocol.ipns.*; - -import java.time.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.*; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.crypto.keys.Ed25519Kt; +import io.libp2p.protocol.Identify; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import org.junit.Assert; +import org.junit.Test; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.config.IdentitySection; +import org.peergos.protocol.dht.RamRecordStore; +import org.peergos.protocol.http.HttpProtocol; +import org.peergos.protocol.ipns.IPNS; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; public class EmbeddedIpfsTest { @@ -35,7 +53,73 @@ public void largeBlock() throws Exception { Cid block = node2.blockstore.put(new byte[1024 * 1024], Cid.Codec.Raw).join(); PeerId peerId2 = node2.node.getPeerId(); List retrieved = ForkJoinPool.commonPool().submit( - () -> node1.getBlocks(List.of(new Want(block)), Set.of(peerId2), false)) + () -> node1.getBlocks(List.of(new Want(block)), Set.of(peerId2), false)) + .get(5, TimeUnit.SECONDS); + Assert.assertTrue(retrieved.size() == 1); + + node1.stop(); + node2.stop(); + } + + @Test + public void largeWrite() throws Exception { + System.setProperty("io.netty.leakDetection.level", "advanced"); + // Start proxy target + InetSocketAddress proxyTarget = new InetSocketAddress("localhost", 7777); + HttpServer target = HttpServer.create(proxyTarget, 20); + String reply = "AllGood"; + byte[] replyBytes = reply.getBytes(); + target.createContext("/", ex -> { + ex.sendResponseHeaders(200, replyBytes.length); + OutputStream out = ex.getResponseBody(); + out.write(replyBytes); + out.flush(); + out.close(); + }); + target.start(); + + HttpProtocol.HttpRequestProcessor http1 = (s, req, h) -> HttpProtocol.proxyRequest(req, proxyTarget, h); + EmbeddedIpfs node1 = build(Collections.emptyList(), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort())), Optional.of(http1)); + node1.start(); + + HttpProtocol.HttpRequestProcessor http2 = (s, req, h) -> HttpProtocol.proxyRequest(req, new InetSocketAddress("localhost", 7778), h); + EmbeddedIpfs node2 = build(node1.node.listenAddresses() + .stream() + .map(a -> new MultiAddress(a.toString())) + .collect(Collectors.toList()), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort())), Optional.of(http2)); + node2.start(); + + for (int i = 0; i < 1000; i++) { + ByteBuf largeBody = Unpooled.buffer(2 * 1024 * 1024); + DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/hey", largeBody); + HttpProtocol.HttpController http = node2.p2pHttp.get().dial(node2.node, node1.node.getPeerId(), node1.node.listenAddresses().toArray(Multiaddr[]::new)) + .getController().join(); + FullHttpResponse resp = http.send(req).join(); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + int contentLength = resp.headers().getInt("content-length"); + resp.content().readBytes(bout, contentLength); + byte[] body = bout.toByteArray(); + Assert.assertTrue("Correct response", Arrays.equals(body, replyBytes)); + resp.release(); + resp.release(); + } + + node1.stop(); + node2.stop(); + } + + @Test + public void mdnsDiscovery() throws Exception { + EmbeddedIpfs node1 = build(Collections.emptyList(), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); + node1.start(); + EmbeddedIpfs node2 = build(Collections.emptyList(), List.of(new MultiAddress("/ip4/127.0.0.1/tcp/" + TestPorts.getPort()))); + node2.start(); + + Thread.sleep(5_000); + Cid block = node2.blockstore.put(new byte[1024], Cid.Codec.Raw).join(); + PeerId peerId2 = node2.node.getPeerId(); + List retrieved = ForkJoinPool.commonPool().submit( + () -> node1.getBlocks(List.of(new Want(block)), Set.of(peerId2), false)) .get(5, TimeUnit.SECONDS); Assert.assertTrue(retrieved.size() == 1); @@ -65,7 +149,7 @@ public void publishPresignedValue() throws Exception { PrivKey publisher = Ed25519Kt.generateEd25519KeyPair().getFirst(); byte[] value = "This is a test".getBytes(); io.ipfs.multihash.Multihash pub = Multihash.deserialize(PeerId.fromPubKey(publisher.publicKey()).getBytes()); - long hoursTtl = 24*365; + long hoursTtl = 24 * 365; LocalDateTime expiry = LocalDateTime.now().plusHours(hoursTtl); long ttlNanos = hoursTtl * 3600_000_000_000L; byte[] signedRecord = IPNS.createSignedRecord(value, expiry, 1, ttlNanos, publisher); @@ -119,12 +203,16 @@ public void wildcardListenerAddressesGetExpanded() { } public static EmbeddedIpfs build(List bootstrap, List swarmAddresses) { + return build(bootstrap, swarmAddresses, Optional.empty()); + } + + public static EmbeddedIpfs build(List bootstrap, List swarmAddresses, Optional http) { BlockRequestAuthoriser blockRequestAuthoriser = (c, p, a) -> CompletableFuture.completedFuture(true); HostBuilder builder = new HostBuilder().generateIdentity(); PrivKey privKey = builder.getPrivateKey(); PeerId peerId = builder.getPeerId(); IdentitySection id = new IdentitySection(privKey.bytes(), peerId); return EmbeddedIpfs.build(new RamRecordStore(), new RamBlockstore(), true, swarmAddresses, bootstrap, - id, blockRequestAuthoriser, Optional.empty(), false); + id, blockRequestAuthoriser, http, false); } } diff --git a/src/test/java/org/peergos/FindPeerTest.java b/src/test/java/org/peergos/FindPeerTest.java index 313e780c..99fc202b 100644 --- a/src/test/java/org/peergos/FindPeerTest.java +++ b/src/test/java/org/peergos/FindPeerTest.java @@ -18,10 +18,10 @@ public class FindPeerTest { public void findLongRunningNode() { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { // bootstrap node 1 @@ -51,7 +51,7 @@ private static long findAndDialPeer(Multihash toFind, Kademlia dht1, Host node1) Multiaddr[] addrs = peer.getPublicAddresses().stream().map(a -> Multiaddr.fromString(a.toString())).toArray(Multiaddr[]::new); dht1.dial(node1, PeerId.fromBase58(peer.peerId.toBase58()), addrs) .getController().join().closerPeers(toFind.toBytes()).join(); - System.out.println("Peer lookup took " + (t2-t1) + "ms"); + System.out.println("Peer lookup took " + (t2 - t1) + "ms"); return t2 - t1; } } diff --git a/src/test/java/org/peergos/FindProviderTest.java b/src/test/java/org/peergos/FindProviderTest.java index 1c724f85..ce563c89 100644 --- a/src/test/java/org/peergos/FindProviderTest.java +++ b/src/test/java/org/peergos/FindProviderTest.java @@ -20,7 +20,7 @@ public class FindProviderTest { public void findBlockProvider() { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); diff --git a/src/test/java/org/peergos/HandlerTest.java b/src/test/java/org/peergos/HandlerTest.java index 3c0027e8..f9ea7894 100644 --- a/src/test/java/org/peergos/HandlerTest.java +++ b/src/test/java/org/peergos/HandlerTest.java @@ -9,17 +9,27 @@ import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import org.peergos.blockstore.*; +import org.peergos.blockstore.Blockstore; +import org.peergos.blockstore.ProvidingBlockstore; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.blockstore.TypeLimitedBlockstore; import org.peergos.client.NabuClient; import org.peergos.net.APIHandler; -import org.peergos.protocol.bitswap.*; +import org.peergos.protocol.bitswap.Bitswap; +import org.peergos.protocol.bitswap.BitswapEngine; import org.peergos.protocol.dht.Kademlia; import org.peergos.protocol.dht.RamProviderStore; import org.peergos.protocol.dht.RamRecordStore; import java.io.IOException; import java.net.InetSocketAddress; -import java.util.*; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.function.Predicate; @@ -46,7 +56,7 @@ public void codecTest() { apiServer = HttpServer.create(localAPIAddress, 500); Blockstore blocks = new TypeLimitedBlockstore(new RamBlockstore(), Set.of(Cid.Codec.Raw)); EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(blocks), null, null, - new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty()); + new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); @@ -78,12 +88,13 @@ public void codecTest() { } } } + @Test @Ignore public void findBlockProviderTest() throws IOException { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); HttpServer apiServer = null; @@ -100,7 +111,7 @@ public void findBlockProviderTest() throws IOException { apiServer = HttpServer.create(localAPIAddress, 500); EmbeddedIpfs ipfs = new EmbeddedIpfs(node1, new ProvidingBlockstore(new RamBlockstore()), null, dht, - null, Optional.empty(), Collections.emptyList(), Optional.empty()); + null, Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); @@ -117,6 +128,7 @@ public void findBlockProviderTest() throws IOException { } } } + @Test public void blockMethodsTest() { HttpServer apiServer = null; @@ -126,7 +138,7 @@ public void blockMethodsTest() { apiServer = HttpServer.create(localAPIAddress, 500); EmbeddedIpfs ipfs = new EmbeddedIpfs(null, new ProvidingBlockstore(new RamBlockstore()), null, - null, new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty()); + null, new Bitswap(new BitswapEngine(null, null, Bitswap.MAX_MESSAGE_SIZE)), Optional.empty(), Collections.emptyList(), Optional.empty(), Collections.emptyList()); apiServer.createContext(APIHandler.API_URL, new APIHandler(ipfs)); apiServer.setExecutor(Executors.newFixedThreadPool(50)); apiServer.start(); @@ -139,7 +151,7 @@ public void blockMethodsTest() { byte[] block = text.getBytes(); Cid addedHash = nabu.putBlock(block, Optional.of("raw")); - int size = nabu.stat(addedHash); + int size = nabu.stat(addedHash); Assert.assertTrue("size as expected", size == text.length()); boolean has = nabu.hasBlock(addedHash, Optional.empty()); diff --git a/src/test/java/org/peergos/HttpProxyTest.java b/src/test/java/org/peergos/HttpProxyTest.java index e5ed575f..bc22c381 100644 --- a/src/test/java/org/peergos/HttpProxyTest.java +++ b/src/test/java/org/peergos/HttpProxyTest.java @@ -1,25 +1,39 @@ package org.peergos; -import com.sun.net.httpserver.*; +import com.sun.net.httpserver.HttpServer; import io.ipfs.cid.Cid; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import io.libp2p.core.multiformats.*; -import io.netty.handler.codec.http.*; -import org.junit.*; -import org.peergos.blockstore.*; -import org.peergos.protocol.*; -import org.peergos.protocol.dht.*; -import org.peergos.protocol.http.*; -import org.peergos.util.*; -import org.peergos.util.HttpUtil; - -import java.io.*; -import java.net.*; -import java.nio.charset.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.zip.*; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.multiformats.Multiaddr; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import org.junit.Assert; +import org.junit.Test; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.protocol.IdentifyBuilder; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.RamProviderStore; +import org.peergos.protocol.dht.RamRecordStore; +import org.peergos.protocol.http.HttpProtocol; +import org.peergos.util.JSONParser; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPInputStream; public class HttpProxyTest { @@ -34,19 +48,19 @@ public void peerid() { public void p2pProxyRequest() throws IOException { InetSocketAddress unusedProxyTarget = new InetSocketAddress("127.0.0.1", 7000); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false) .addProtocol(new HttpProtocol.Binding(unusedProxyTarget)); Host node1 = builder1.build(); InetSocketAddress proxyTarget = new InetSocketAddress("127.0.0.1", TestPorts.getPort()); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false) .addProtocol(new HttpProtocol.Binding(proxyTarget)); Host node2 = builder2.build(); node1.start().join(); node2.start().join(); // start local server with fixed HTTP response - byte[] httpReply = new byte[1024*1024]; + byte[] httpReply = new byte[1024 * 1024]; new Random(42).nextBytes(httpReply); HttpServer localhostServer = HttpServer.create(proxyTarget, 20); String headerName = "Random-Header"; @@ -79,7 +93,7 @@ public void p2pProxyRequest() throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); resp.content().readBytes(bout, resp.headers().getInt("content-length")); - Assert.assertTrue(resp.headers().get(headerName).equals(headerValue)); + Assert.assertEquals(resp.headers().get(headerName), headerValue); resp.release(); byte[] replyBody = bout.toByteArray(); equal(replyBody, httpReply); @@ -95,11 +109,11 @@ public void p2pProxyRequest() throws IOException { public void p2pProxyClientTest() throws Exception { InetSocketAddress unusedProxyTarget = new InetSocketAddress("127.0.0.1", 7000); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false) .addProtocol(new HttpProtocol.Binding(unusedProxyTarget)); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); Kademlia dht = builder1.getWanDht().get(); dht.bootstrapRoutingTable(node1, BootstrapTest.BOOTSTRAP_NODES, a -> true); System.out.println("Bootstrapping node..."); @@ -136,7 +150,7 @@ public void p2pProxyClientTest() throws Exception { GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(bout.toByteArray())); - List reply = (List)JSONParser.parse(new String(readFully(gzip))); + List reply = (List) JSONParser.parse(new String(readFully(gzip))); Assert.assertTrue(reply.contains("peergos")); } System.out.println("Average: " + totalTime / count); @@ -146,10 +160,10 @@ public void p2pProxyClientTest() throws Exception { } public static byte[] readFully(InputStream in) throws IOException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - byte[] b = new byte[0x1000]; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] b = new byte[0x1000]; int nRead; - while ((nRead = in.read(b, 0, b.length)) != -1 ) + while ((nRead = in.read(b, 0, b.length)) != -1) bout.write(b, 0, nRead); in.close(); return bout.toByteArray(); @@ -161,9 +175,9 @@ private static void equal(byte[] a, byte[] b) { for (int i = 0; i < a.length; i++) if (a[i] != b[i]) { byte[] diff = Arrays.copyOfRange(a, i, i + 24); - int j=0; - for (;j < b.length-2;j++) - if (b[j] == diff[0] && b[j+1] == diff[1]&& b[j+2] == diff[2]) + int j = 0; + for (; j < b.length - 2; j++) + if (b[j] == diff[0] && b[j + 1] == diff[1] && b[j + 2] == diff[2]) break; throw new IllegalStateException("bytes differ at " + i + " " + a[i] + " != " + b[i]); } diff --git a/src/test/java/org/peergos/IpnsTest.java b/src/test/java/org/peergos/IpnsTest.java index adbe96e5..e809bd3f 100644 --- a/src/test/java/org/peergos/IpnsTest.java +++ b/src/test/java/org/peergos/IpnsTest.java @@ -23,15 +23,15 @@ public class IpnsTest { public void publishIPNSRecordToKubo() throws IOException { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); Multihash node1Id = Multihash.deserialize(node1.getPeerId().getBytes()); try { IPFS kubo = new IPFS("localhost", 5001); - String kuboID = (String)kubo.id().get("ID"); + String kuboID = (String) kubo.id().get("ID"); Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kuboID); Cid block = blockstore1.put("Provide me.".getBytes(), Cid.Codec.Raw).join(); Kademlia dht = builder1.getWanDht().get(); @@ -55,10 +55,10 @@ public void publishIPNSRecordToKubo() throws IOException { } catch (Exception timeout) { } } - if (! success) + if (!success) throw new IllegalStateException("Failed to publish IPNS record!"); GetResult getresult = dht.dial(node1, address2).getController().join().getValue(node1Id).join(); - if (! getresult.record.isPresent()) + if (!getresult.record.isPresent()) throw new IllegalStateException("Kubo didn't return our published IPNS record!"); } finally { node1.stop(); @@ -70,14 +70,14 @@ public void publishIPNSRecordToKubo() throws IOException { public void retrieveKuboPublishedIPNS() throws IOException { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { IPFS kubo = new IPFS("localhost", 5001); - String kuboIDString = (String)kubo.id().get("ID"); + String kuboIDString = (String) kubo.id().get("ID"); Multihash kuboId = Multihash.fromBase58(kuboIDString); Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kuboIDString); @@ -88,7 +88,8 @@ public void retrieveKuboPublishedIPNS() throws IOException { GetResult kuboIpnsGet = wanDht.dial(node1, address2).getController().join().getValue(kuboId).join(); LinkedBlockingDeque queue = new LinkedBlockingDeque<>(); queue.addAll(kuboIpnsGet.closerPeers); - outer: for (int i=0; i < 100; i++) { + outer: + for (int i = 0; i < 100; i++) { if (kuboIpnsGet.record.isPresent()) break; PeerAddresses closer = queue.poll(); @@ -96,7 +97,7 @@ public void retrieveKuboPublishedIPNS() throws IOException { .map(a -> a.toString()) .filter(a -> a.contains("tcp") && a.contains("ip4") && !a.contains("127.0.0.1") && !a.contains("/172.")) .collect(Collectors.toList()); - for (String candidate: candidates) { + for (String candidate : candidates) { try { kuboIpnsGet = wanDht.dial(node1, Multiaddr.fromString(candidate + "/p2p/" + closer.peerId)).getController().join() .getValue(kuboId).join(); diff --git a/src/test/java/org/peergos/KademliaTest.java b/src/test/java/org/peergos/KademliaTest.java index df9bcbb8..90820955 100644 --- a/src/test/java/org/peergos/KademliaTest.java +++ b/src/test/java/org/peergos/KademliaTest.java @@ -1,18 +1,28 @@ package org.peergos; -import io.ipfs.cid.*; +import io.ipfs.cid.Cid; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import io.libp2p.core.crypto.*; -import io.libp2p.core.multiformats.*; -import io.libp2p.crypto.keys.*; -import org.junit.*; -import org.peergos.blockstore.*; -import org.peergos.protocol.*; -import org.peergos.protocol.dht.*; - -import java.util.*; -import java.util.concurrent.*; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.crypto.keys.Ed25519Kt; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.protocol.IdentifyBuilder; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.KademliaEngine; +import org.peergos.protocol.dht.RamProviderStore; +import org.peergos.protocol.dht.RamRecordStore; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; public class KademliaTest { @@ -20,16 +30,16 @@ public class KademliaTest { public void findOtherNode() throws Exception { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node2 = builder2.build(); node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node2); + IdentifyBuilder.addIdentifyProtocol(node2, Collections.emptyList()); try { // bootstrap node 2 @@ -61,16 +71,16 @@ public void findOtherNode() throws Exception { public void ipnsBenchmark() throws Exception { RamBlockstore blockstore1 = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore1, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node2 = builder2.build(); node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node2); + IdentifyBuilder.addIdentifyProtocol(node2, Collections.emptyList()); Cid value = blockstore1.put("Publish me.".getBytes(), Cid.Codec.Raw).join(); @@ -96,8 +106,8 @@ public void ipnsBenchmark() throws Exception { long p0 = System.currentTimeMillis(); int publishes = dht1.publishIpnsValue(signer, pub, value, 1, node1).join(); long p1 = System.currentTimeMillis(); - System.out.println("Publish took " + printSeconds(p1-p0) + "s to " + publishes + " peers."); - publishTotal += p1-p0; + System.out.println("Publish took " + printSeconds(p1 - p0) + "s to " + publishes + " peers."); + publishTotal += p1 - p0; // retrieve it from node 2 long t0 = System.currentTimeMillis(); @@ -105,10 +115,10 @@ public void ipnsBenchmark() throws Exception { long t1 = System.currentTimeMillis(); Assert.assertTrue(res.equals("/ipfs/" + value)); System.out.println("Resolved in " + printSeconds(t1 - t0) + "s"); - resolveTotal += t1-t0; + resolveTotal += t1 - t0; } - System.out.println("Publish av: " + printSeconds(publishTotal/iterations) - + ", resolve av: " + printSeconds(resolveTotal/iterations)); + System.out.println("Publish av: " + printSeconds(publishTotal / iterations) + + ", resolve av: " + printSeconds(resolveTotal / iterations)); // retrieve all again for (PrivKey signer : signers) { @@ -126,7 +136,7 @@ public void ipnsBenchmark() throws Exception { } public static String printSeconds(long millis) { - return millis / 1000 + "." + (millis % 1000)/100; + return millis / 1000 + "." + (millis % 1000) / 100; } @Test @@ -136,9 +146,9 @@ public void kademliaFindNodeLimitTest() { new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore()); RamAddressBook addrs = new RamAddressBook(); kad.setAddressBook(addrs); - for (int i=0; i < 1000; i++) { + for (int i = 0; i < 1000; i++) { PeerId peer = new HostBuilder().generateIdentity().getPeerId(); - for (int j=0; j < 100; j++) { + for (int j = 0; j < 100; j++) { kad.addIncomingConnection(peer); addrs.addAddrs(peer, 0, new Multiaddr[]{new Multiaddr("/ip4/127.0.0.1/tcp/4001/p2p/" + peer.toBase58())}); } diff --git a/src/test/java/org/peergos/KuboFindProviderTest.java b/src/test/java/org/peergos/KuboFindProviderTest.java index bf811e5a..f9c9a9b2 100644 --- a/src/test/java/org/peergos/KuboFindProviderTest.java +++ b/src/test/java/org/peergos/KuboFindProviderTest.java @@ -20,11 +20,11 @@ public class KuboFindProviderTest { @Test public void findProviderOverYamux() throws IOException { HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), new RamProviderStore(1000), - new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false) + new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false) .addMuxers(List.of(StreamMuxerProtocol.getYamux())); Host node1 = builder1.build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { IPFS kubo = new IPFS("localhost", 5001); Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); diff --git a/src/test/java/org/peergos/KuboPingTest.java b/src/test/java/org/peergos/KuboPingTest.java index ff8b00ae..da976b4f 100644 --- a/src/test/java/org/peergos/KuboPingTest.java +++ b/src/test/java/org/peergos/KuboPingTest.java @@ -26,7 +26,7 @@ public void runPingOverYamux() throws IOException { .addMuxers(List.of(StreamMuxerProtocol.getYamux())) .build(); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { IPFS kubo = new IPFS("localhost", 5001); Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); diff --git a/src/test/java/org/peergos/KuboTest.java b/src/test/java/org/peergos/KuboTest.java index e1041662..36b88b86 100644 --- a/src/test/java/org/peergos/KuboTest.java +++ b/src/test/java/org/peergos/KuboTest.java @@ -21,7 +21,7 @@ public void getBlock() throws IOException { Bitswap bitswap1 = new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)); Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(bitswap1)); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); try { IPFS kubo = new IPFS("localhost", 5001); Multiaddr address2 = Multiaddr.fromString("/ip4/127.0.0.1/tcp/4001/p2p/" + kubo.id().get("ID")); diff --git a/src/test/java/org/peergos/P2pHttpChatTest.java b/src/test/java/org/peergos/P2pHttpChatTest.java index bb4db223..5f3eb314 100644 --- a/src/test/java/org/peergos/P2pHttpChatTest.java +++ b/src/test/java/org/peergos/P2pHttpChatTest.java @@ -25,7 +25,7 @@ public void p2pHttpChat() { h.accept(replyOk.retain()); }); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false) .addProtocol(node1Http); Host node1 = builder1.build(); HttpProtocol.Binding node2Http = new HttpProtocol.Binding((s, req, h) -> { @@ -34,7 +34,7 @@ public void p2pHttpChat() { h.accept(replyOk.retain()); }); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false) + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false) .addProtocol(node2Http); Host node2 = builder2.build(); node1.start().join(); @@ -75,6 +75,7 @@ public void p2pHttpChat() { node2.stop(); } } + public static void printBody(HttpRequest req) { if (req instanceof FullHttpRequest) { ByteBuf content = ((FullHttpRequest) req).content(); diff --git a/src/test/java/org/peergos/P2pHttpTest.java b/src/test/java/org/peergos/P2pHttpTest.java index 7282464d..586b0db3 100644 --- a/src/test/java/org/peergos/P2pHttpTest.java +++ b/src/test/java/org/peergos/P2pHttpTest.java @@ -37,7 +37,7 @@ public void p2pTest() { h.accept(emptyReply.retain()); }); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false, false); builder1 = builder1.addProtocol(node1Http); Host node1 = builder1.build(); node1.start().join(); @@ -73,7 +73,7 @@ public void p2pTest() { HttpProtocol.proxyRequest(req, new InetSocketAddress("127.0.0.1", localPort), h); }); HostBuilder builder2 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore2, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); builder2 = builder2.addProtocol(node2Http); Host node2 = builder2.build(); node2.start().join(); diff --git a/src/test/java/org/peergos/PingTest.java b/src/test/java/org/peergos/PingTest.java index 637a39a4..d52056c3 100644 --- a/src/test/java/org/peergos/PingTest.java +++ b/src/test/java/org/peergos/PingTest.java @@ -1,21 +1,28 @@ package org.peergos; -import identify.pb.*; -import io.ipfs.multiaddr.*; +import identify.pb.IdentifyOuterClass; +import io.ipfs.multiaddr.MultiAddress; import io.ipfs.multihash.Multihash; -import io.libp2p.core.*; -import io.libp2p.core.crypto.*; -import io.libp2p.core.multiformats.*; -import io.libp2p.core.multistream.*; -import io.libp2p.crypto.keys.*; -import io.libp2p.protocol.*; -import org.junit.*; -import org.peergos.blockstore.*; -import org.peergos.protocol.*; -import org.peergos.protocol.bitswap.*; +import io.libp2p.core.Host; +import io.libp2p.core.crypto.PrivKey; +import io.libp2p.core.multiformats.Multiaddr; +import io.libp2p.core.multistream.ProtocolBinding; +import io.libp2p.crypto.keys.Ed25519Kt; +import io.libp2p.crypto.keys.RsaKt; +import io.libp2p.protocol.Identify; +import io.libp2p.protocol.IdentifyController; +import io.libp2p.protocol.Ping; +import io.libp2p.protocol.PingController; +import org.junit.Assert; +import org.junit.Test; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.protocol.IdentifyBuilder; +import org.peergos.protocol.bitswap.Bitswap; +import org.peergos.protocol.bitswap.BitswapEngine; -import java.util.*; -import java.util.concurrent.*; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; public class PingTest { @@ -62,8 +69,8 @@ public void runPingEd25519ToRSA() { new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); - IdentifyBuilder.addIdentifyProtocol(node2); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); + IdentifyBuilder.addIdentifyProtocol(node2, Collections.emptyList()); Assert.assertTrue(new Multihash(Multihash.Type.id, node1Keys.publicKey().bytes()).toString().equals(node1.getPeerId().toString())); Assert.assertTrue(new Multihash(Multihash.Type.sha2_256, Hash.sha256(node2Keys.publicKey().bytes())).toString().equals(node2.getPeerId().toString())); @@ -87,9 +94,9 @@ public void replyIdentifyOnNewDial() { Host node1 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); Host node2 = HostBuilder.build(TestPorts.getPort(), List.of(new Ping(), new Bitswap(new BitswapEngine(new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), Bitswap.MAX_MESSAGE_SIZE)))); node1.start().join(); - IdentifyBuilder.addIdentifyProtocol(node1); + IdentifyBuilder.addIdentifyProtocol(node1, Collections.emptyList()); node2.start().join(); - IdentifyBuilder.addIdentifyProtocol(node2); + IdentifyBuilder.addIdentifyProtocol(node2, Collections.emptyList()); try { // ping from 1 to 2 Multiaddr address2 = node2.listenAddresses().get(0); diff --git a/src/test/java/org/peergos/ProvideTest.java b/src/test/java/org/peergos/ProvideTest.java index dff9ad9f..b13c338f 100644 --- a/src/test/java/org/peergos/ProvideTest.java +++ b/src/test/java/org/peergos/ProvideTest.java @@ -1,17 +1,20 @@ package org.peergos; -import io.ipfs.cid.*; -import io.ipfs.multiaddr.*; -import io.ipfs.multihash.*; -import io.libp2p.core.*; -import org.junit.*; -import org.peergos.blockstore.*; -import org.peergos.protocol.dht.*; +import io.ipfs.cid.Cid; +import io.ipfs.multiaddr.MultiAddress; +import io.ipfs.multihash.Multihash; +import io.libp2p.core.Host; +import org.junit.Ignore; +import org.junit.Test; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.RamProviderStore; +import org.peergos.protocol.dht.RamRecordStore; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; -import java.util.stream.*; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import java.util.stream.Collectors; public class ProvideTest { @@ -20,7 +23,7 @@ public class ProvideTest { public void provideBlock() { RamBlockstore blockstore = new RamBlockstore(); HostBuilder builder1 = HostBuilder.create(TestPorts.getPort(), - new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), blockstore, (c, p, a) -> CompletableFuture.completedFuture(true), false, false); Host node1 = builder1.build(); node1.start().join(); Multihash node1Id = Multihash.deserialize(node1.getPeerId().getBytes()); diff --git a/src/test/java/org/peergos/RelayTest.java b/src/test/java/org/peergos/RelayTest.java index f66790d6..8e27328d 100644 --- a/src/test/java/org/peergos/RelayTest.java +++ b/src/test/java/org/peergos/RelayTest.java @@ -1,17 +1,24 @@ package org.peergos; -import io.ipfs.multiaddr.*; -import io.libp2p.core.*; -import io.libp2p.core.multiformats.*; -import org.junit.*; -import org.peergos.blockstore.*; -import org.peergos.protocol.circuit.*; -import org.peergos.protocol.dht.*; +import io.ipfs.multiaddr.MultiAddress; +import io.libp2p.core.Host; +import io.libp2p.core.PeerId; +import io.libp2p.core.multiformats.Multiaddr; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.peergos.blockstore.RamBlockstore; +import org.peergos.protocol.circuit.CircuitHopProtocol; +import org.peergos.protocol.circuit.Relay; +import org.peergos.protocol.dht.Kademlia; +import org.peergos.protocol.dht.RamProviderStore; +import org.peergos.protocol.dht.RamRecordStore; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; -import java.util.stream.*; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import java.util.stream.Collectors; public class RelayTest { @@ -19,11 +26,11 @@ public class RelayTest { @Ignore // needs fixed find providers public void relay() { HostBuilder builder1 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true),false, false); Host node1 = builder1.build(); node1.start().join(); HostBuilder builder2 = HostBuilder.create(10000 + new Random().nextInt(50000), - new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true), false); + new RamProviderStore(1000), new RamRecordStore(), new RamBlockstore(), (c, p, a) -> CompletableFuture.completedFuture(true),false, false); Host node2 = builder2.build(); node2.start().join(); diff --git a/src/test/java/org/peergos/blockstore/BloomTest.java b/src/test/java/org/peergos/blockstore/BloomTest.java index 6ef62c5f..5ffa771c 100644 --- a/src/test/java/org/peergos/blockstore/BloomTest.java +++ b/src/test/java/org/peergos/blockstore/BloomTest.java @@ -33,6 +33,10 @@ public void bloom() { long t4 = System.currentTimeMillis(); System.out.println("Doubling filter size took: " + (t4-t3)+ "ms"); + for (Cid ref : bs.refs(false).join()) { + Assert.assertTrue(filtered.has(ref).join()); + } + checkFalsePositiveRate(bloom, 14); }

-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 z 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