From df73c85926ef27e6930993a70b389eec3b4657ba Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 19 Jan 2026 15:55:13 +0100 Subject: [PATCH 1/8] Update Kubo version From ancient 0.18.1 to latest 0.39.0. This causes test breakage as some deprecated endpoints like `object` are dropped. --- install-run-ipfs.sh | 6 +++--- mac-install-run-ipfs.sh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/install-run-ipfs.sh b/install-run-ipfs.sh index a4bf3c8..f40377d 100755 --- a/install-run-ipfs.sh +++ b/install-run-ipfs.sh @@ -1,6 +1,6 @@ #! /bin/sh -wget https://dist.ipfs.io/kubo/v0.18.1/kubo_v0.18.1_linux-amd64.tar.gz -O /tmp/kubo_linux-amd64.tar.gz +wget https://dist.ipfs.io/kubo/v0.39.0/kubo_v0.39.0_linux-amd64.tar.gz -O /tmp/kubo_linux-amd64.tar.gz tar -xvf /tmp/kubo_linux-amd64.tar.gz export PATH=$PATH:$PWD/kubo/ -ipfs init -ipfs daemon --enable-pubsub-experiment --routing=dhtclient & +ipfs init --profile server +ipfs daemon --enable-pubsub-experiment --enable-namesys-pubsub --routing=dhtclient & diff --git a/mac-install-run-ipfs.sh b/mac-install-run-ipfs.sh index dd46049..7c8538e 100755 --- a/mac-install-run-ipfs.sh +++ b/mac-install-run-ipfs.sh @@ -1,6 +1,6 @@ #! /bin/sh -wget https://dist.ipfs.io/kubo/v0.18.1/kubo_v0.18.1_darwin-arm64.tar.gz -O /tmp/kubo_darwin-arm64.tar.gz +wget https://dist.ipfs.io/kubo/v0.39.0/kubo_v0.39.0_darwin-arm64.tar.gz -O /tmp/kubo_darwin-arm64.tar.gz tar -xvf /tmp/kubo_darwin-arm64.tar.gz export PATH=$PATH:$PWD/kubo/ -ipfs init -ipfs daemon --enable-pubsub-experiment --routing=dhtclient & +ipfs init --profile server +ipfs daemon --enable-pubsub-experiment --enable-namesys-pubsub --routing=dhtclient & From 3b77fca062cc837ba31c07c4eba1302319150620 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 19 Jan 2026 16:00:39 +0100 Subject: [PATCH 2/8] Fix for GH workflow: double triggered CI Once for push and once for pull_request. Observe push ONLY on master --- .github/workflows/ant.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ant.yml b/.github/workflows/ant.yml index d71b6d3..6984d6d 100644 --- a/.github/workflows/ant.yml +++ b/.github/workflows/ant.yml @@ -3,14 +3,14 @@ name: Java CI on: schedule: - cron: '42 0 * * 4' - push: + push: + branches: + - master pull_request: jobs: build: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v6 - name: Set up JDK 11 From b67fe7ddc00ed87a3c6eb909482dd3ede72991b4 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 19 Jan 2026 18:24:29 +0100 Subject: [PATCH 3/8] Fix UTs --- src/main/java/io/ipfs/api/IPFS.java | 2 +- src/main/java/io/ipfs/api/Peer.java | 11 +++++- src/test/java/io/ipfs/api/APITest.java | 54 +++++++++++++++++--------- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/ipfs/api/IPFS.java b/src/main/java/io/ipfs/api/IPFS.java index 60b1067..1cbdbea 100644 --- a/src/main/java/io/ipfs/api/IPFS.java +++ b/src/main/java/io/ipfs/api/IPFS.java @@ -832,7 +832,7 @@ public List add() throws IOException { } public List list() throws IOException { - return ((List)retrieveMap("bootstrap/list").get("Peers")) + return ((List)retrieveMap("bootstrap/list?expand-auto=true").get("Peers")) .stream().map(x -> new MultiAddress(x)).collect(Collectors.toList()); } diff --git a/src/main/java/io/ipfs/api/Peer.java b/src/main/java/io/ipfs/api/Peer.java index 551011c..b8555b3 100644 --- a/src/main/java/io/ipfs/api/Peer.java +++ b/src/main/java/io/ipfs/api/Peer.java @@ -27,8 +27,15 @@ public static Peer fromJSON(Object json) { throw new IllegalStateException("Incorrect json for Peer: " + JSONParser.toString(json)); Map m = (Map) json; Function val = key -> (String) m.get(key); - long latency = val.apply("Latency").length() > 0 ? Long.parseLong(val.apply("Latency")) : -1; - return new Peer(new MultiAddress(val.apply("Addr")), Cid.decode(val.apply("Peer")), latency, val.apply("Muxer"), val.apply("Streams")); + String peerId = val.apply("Peer"); + Multihash peer; // Multihash bug? Throws IAEx Base1 not supported + try { + peer = Multihash.fromBase58(peerId); + } catch (Exception e) { + peer = Multihash.decode(peerId); + } + long latency = m.containsKey("Latency") ? Long.parseLong(val.apply("Latency")) : -1; + return new Peer(new MultiAddress(val.apply("Addr")), peer, latency, val.apply("Muxer"), val.apply("Streams")); } @Override diff --git a/src/test/java/io/ipfs/api/APITest.java b/src/test/java/io/ipfs/api/APITest.java index 2b291f3..788dc25 100644 --- a/src/test/java/io/ipfs/api/APITest.java +++ b/src/test/java/io/ipfs/api/APITest.java @@ -7,6 +7,7 @@ import org.junit.*; import java.io.*; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.*; import java.util.function.*; @@ -396,31 +397,40 @@ public void rawLeafNodePinUpdate() throws IOException { @Test public void indirectPinTest() throws IOException { - Multihash EMPTY = ipfs.object._new(Optional.empty()).hash; - io.ipfs.api.MerkleNode data = ipfs.object.patch(EMPTY, "set-data", Optional.of("childdata".getBytes()), Optional.empty(), Optional.empty()); - Multihash child = data.hash; + String path = "/test/indirectPinTest-" + UUID.randomUUID(); + ipfs.files.write(path + "/content", new NamedStreamable.ByteArrayWrapper("something".getBytes(StandardCharsets.UTF_8)), true, true); + Multihash content = Multihash.decode((String) ipfs.files.stat(path + "/content").get("Hash")); - io.ipfs.api.MerkleNode tmp1 = ipfs.object.patch(EMPTY, "set-data", Optional.of("parent1_data".getBytes()), Optional.empty(), Optional.empty()); - Multihash parent1 = ipfs.object.patch(tmp1.hash, "add-link", Optional.empty(), Optional.of(child.toString()), Optional.of(child)).hash; - ipfs.pin.add(parent1); + // adding one more extra entry to parent1 to keep its hash different from parent2 + ipfs.files.mkdir(path + "/parent1", true); + ipfs.files.write(path + "/parent1/content1", new NamedStreamable.ByteArrayWrapper("somethingelse".getBytes(StandardCharsets.UTF_8)), true, true); + ipfs.files.cp("/ipfs/" + content, path + "/parent1/content2", true); + + ipfs.files.mkdir(path + "/parent2", true); + ipfs.files.cp("/ipfs/" + content, path + "/parent2/content", true); - io.ipfs.api.MerkleNode tmp2 = ipfs.object.patch(EMPTY, "set-data", Optional.of("parent2_data".getBytes()), Optional.empty(), Optional.empty()); - Multihash parent2 = ipfs.object.patch(tmp2.hash, "add-link", Optional.empty(), Optional.of(child.toString()), Optional.of(child)).hash; + Multihash parent1 = Multihash.decode((String) ipfs.files.stat(path + "/parent1").get("Hash")); + Multihash parent2 = Multihash.decode((String) ipfs.files.stat(path + "/parent2").get("Hash")); + + ipfs.pin.add(parent1); ipfs.pin.add(parent2); ipfs.pin.rm(parent1, true); Map ls = ipfs.pin.ls(IPFS.PinType.all); - boolean childPresent = ls.containsKey(child); + boolean childPresent = ls.containsKey(content); if (!childPresent) - throw new IllegalStateException("Child not present!"); + throw new IllegalStateException("Child not present: " + ls); ipfs.repo.gc(); Map ls2 = ipfs.pin.ls(IPFS.PinType.all); - boolean childPresentAfterGC = ls2.containsKey(child); + boolean childPresentAfterGC = ls2.containsKey(content); if (!childPresentAfterGC) - throw new IllegalStateException("Child not present!"); -} + throw new IllegalStateException("Child not present:" + ls); + ipfs.files.rm(path, true, true); + } + + @Ignore("RPC API removed") @Test public void objectPatch() throws IOException { MerkleNode obj = ipfs.object._new(Optional.empty()); @@ -462,6 +472,7 @@ public void refsTest() throws IOException { } } + @Ignore("RPC API removed") @Test public void objectTest() throws IOException { MerkleNode _new = ipfs.object._new(Optional.empty()); @@ -489,10 +500,8 @@ public void bulkBlockTest() throws IOException { List bulkPut = ipfs.block.put(Arrays.asList(raw, raw, raw, raw, raw), Optional.of("cbor")); List hashes = bulkPut.stream().map(m -> m.hash).collect(Collectors.toList()); byte[] result = ipfs.block.get(hashes.get(0)); - System.out.println(); } -// @Ignore // Ignored because ipfs frequently times out internally in the publish call @Test public void publish() throws Exception { // JSON document @@ -514,6 +523,15 @@ public void publish() throws Exception { assertEquals("Should be equals", resolved, "/ipfs/" + merkleNode.hash); } + @Test + public void resolveName() throws Exception { + // Resolve from DNSLinked domain + String resolved = ipfs.name.resolve("docs.ipfs.tech"); + assertNotNull(resolved); + assertTrue(resolved.startsWith("/ipfs/")); + assertTrue(resolved.length() > 20); // this may change (content and encoding as well) + } + @Test public void pubsubSynchronous() { String topic = "topic" + System.nanoTime(); @@ -587,7 +605,6 @@ public void merkleLinkInMap() throws IOException { // These commands can be used to reproduce this on the command line String reproCommand1 = "printf \"" + toEscapedHex(rawTarget) + "\" | ipfs block put --format=cbor"; String reproCommand2 = "printf \"" + toEscapedHex(rawSource) + "\" | ipfs block put --format=cbor"; - System.out.println(); } @Test @@ -652,7 +669,6 @@ public void rootMerkleLink() throws IOException { // These commands can be used to reproduce this on the command line String reproCommand1 = "printf \"" + toEscapedHex(rawTarget) + "\" | ipfs block put --format=cbor"; String reproCommand2 = "printf \"" + toEscapedHex(obj2) + "\" | ipfs block put --format=cbor"; - System.out.println(); } /** @@ -672,7 +688,6 @@ public void rootNull() throws IOException { // These commands can be used to reproduce this on the command line String reproCommand1 = "printf \"" + toEscapedHex(obj) + "\" | ipfs block put --format=cbor"; - System.out.println(); } /** @@ -754,7 +769,6 @@ public void dhtTest() throws IOException { @Test public void localId() throws Exception { Map id = ipfs.id(); - System.out.println(); } @Test @@ -856,6 +870,8 @@ public void bitswapTest() throws IOException { Map stat = ipfs.bitswap.stat(); Map stat2 = ipfs.bitswap.stat(true); } + + @Ignore("AutoConf.Enabled=true is default; prevents bootstrap removal") @Test public void bootstrapTest() throws IOException { List bootstrap = ipfs.bootstrap.list(); From aa46c630ab330d6459b0e404e7649885b644ef04 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 19 Jan 2026 18:40:30 +0100 Subject: [PATCH 4/8] Rework UT a bit (we use server profile) --- src/test/java/io/ipfs/api/APITest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/ipfs/api/APITest.java b/src/test/java/io/ipfs/api/APITest.java index 788dc25..4ea3b16 100644 --- a/src/test/java/io/ipfs/api/APITest.java +++ b/src/test/java/io/ipfs/api/APITest.java @@ -829,13 +829,15 @@ public void versionTest() throws IOException { @Test public void swarmTestFilters() throws IOException { + // on GH CI we run this in "server" profile that packs a TON of filters + // See https://github.com/ipfs/kubo/blob/c1fd4d70f58e682bfe73fa4b50d17581c823c671/config/profile.go#L27 Map listenAddrs = ipfs.swarm.listenAddrs(); Map localAddrs = ipfs.swarm.localAddrs(true); String multiAddrFilter = "/ip4/192.168.0.0/ipcidr/16"; Map rm = ipfs.swarm.rmFilter(multiAddrFilter); Map filters = ipfs.swarm.filters(); List filtersList = (List)filters.get("Strings"); - Assert.assertNull("Filters empty", filtersList); + Assert.assertTrue("Filters empty", filtersList == null || !filtersList.contains(multiAddrFilter)); Map added = ipfs.swarm.addFilter(multiAddrFilter); filters = ipfs.swarm.filters(); From 532811cffe50c256ab9c58eff2e57249412185b6 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Tue, 20 Jan 2026 12:47:45 +0100 Subject: [PATCH 5/8] Unrelated/undo --- .github/workflows/ant.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ant.yml b/.github/workflows/ant.yml index 6984d6d..4b94883 100644 --- a/.github/workflows/ant.yml +++ b/.github/workflows/ant.yml @@ -4,13 +4,13 @@ on: schedule: - cron: '42 0 * * 4' push: - branches: - - master pull_request: jobs: build: + runs-on: ubuntu-latest + steps: - uses: actions/checkout@v6 - name: Set up JDK 11 @@ -22,8 +22,3 @@ jobs: run: ./install-run-ipfs.sh - name: Build & run tests with Maven run: mvn test - - name: Build with Ant - run: ant -noinput -buildfile build.xml dist - - name: Run tests - timeout-minutes: 10 - run: ant -noinput -buildfile build.xml test From 246722d3d70c233ccdc7579d2c48ccb169441928 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Tue, 20 Jan 2026 12:55:11 +0100 Subject: [PATCH 6/8] Borrow peerId decode --- src/main/java/io/ipfs/api/Peer.java | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/ipfs/api/Peer.java b/src/main/java/io/ipfs/api/Peer.java index b8555b3..9dd42b7 100644 --- a/src/main/java/io/ipfs/api/Peer.java +++ b/src/main/java/io/ipfs/api/Peer.java @@ -2,6 +2,7 @@ import io.ipfs.cid.*; import io.ipfs.multiaddr.*; +import io.ipfs.multibase.Base58; import io.ipfs.multihash.*; import java.util.*; @@ -9,12 +10,12 @@ public class Peer { public final MultiAddress address; - public final Multihash id; + public final Cid id; public final long latency; public final String muxer; public final Object streams; - public Peer(MultiAddress address, Multihash id, long latency, String muxer, Object streams) { + public Peer(MultiAddress address, Cid id, long latency, String muxer, Object streams) { this.address = address; this.id = id; this.latency = latency; @@ -27,17 +28,20 @@ public static Peer fromJSON(Object json) { throw new IllegalStateException("Incorrect json for Peer: " + JSONParser.toString(json)); Map m = (Map) json; Function val = key -> (String) m.get(key); - String peerId = val.apply("Peer"); - Multihash peer; // Multihash bug? Throws IAEx Base1 not supported - try { - peer = Multihash.fromBase58(peerId); - } catch (Exception e) { - peer = Multihash.decode(peerId); - } + Cid peer = decodePeerId(val.apply("Peer")); long latency = m.containsKey("Latency") ? Long.parseLong(val.apply("Latency")) : -1; return new Peer(new MultiAddress(val.apply("Addr")), peer, latency, val.apply("Muxer"), val.apply("Streams")); } + // See https://github.com/Peergos/Peergos/blob/81064fdb2cdf6b6fe126cf6a20d4d40ecd148938/src/peergos/shared/io/ipfs/Cid.java#L148 + public static Cid decodePeerId(String peerId) { + if (peerId.startsWith("1")) { + // convert base58 encoded identity multihash to cidV1 + Multihash hash = Multihash.deserialize(Base58.decode(peerId)); + return new Cid(1, Cid.Codec.Libp2pKey, hash.getType(), hash.getHash()); + } + return Cid.decode(peerId); + } @Override public String toString() { return id + "@" + address; From 676a5a128acbdb6597ce7ca656a77e2c9c2931be Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Tue, 20 Jan 2026 12:57:51 +0100 Subject: [PATCH 7/8] Update docker compose --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4086f43..5d9dbe0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,9 @@ version: '2' services: ipfs-daemon: - image: 'ipfs/kubo:v0.18.1' + image: 'ipfs/kubo:v0.39.0' ports: - "4001:4001" - "5001:5001" user: "ipfs" - command: [ "daemon", "--enable-pubsub-experiment" ] + command: [ "daemon", "--enable-pubsub-experiment --enable-namesys-pubsub --routing=dhtclient" ] From 1af4382c86696e2bb11468b76e559f843e377b96 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Tue, 20 Jan 2026 13:09:25 +0100 Subject: [PATCH 8/8] Fix command array --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5d9dbe0..36d0076 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,4 +6,4 @@ services: - "4001:4001" - "5001:5001" user: "ipfs" - command: [ "daemon", "--enable-pubsub-experiment --enable-namesys-pubsub --routing=dhtclient" ] + command: [ "daemon", "--enable-pubsub-experiment", "--enable-namesys-pubsub", "--routing=dhtclient" ]