Skip to content

Commit

Permalink
Generalise IPNS api to allow publishing pre-signed values
Browse files Browse the repository at this point in the history
  • Loading branch information
ianopolous committed Dec 13, 2023
1 parent c1f2a46 commit 457c7a6
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 45 deletions.
7 changes: 6 additions & 1 deletion src/main/java/org/peergos/EmbeddedIpfs.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ public CompletableFuture<Void> 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<Void> publishPresignedRecord(Multihash pub, byte[] presignedRecord) {
return dht.publishValue(pub, presignedRecord, node);
}

public CompletableFuture<byte[]> resolveValue(PubKey pub) {
Expand Down
26 changes: 9 additions & 17 deletions src/main/java/org/peergos/protocol/dht/Kademlia.java
Original file line number Diff line number Diff line change
Expand Up @@ -313,31 +313,23 @@ public CompletableFuture<Void> 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<Void> publishValue(PrivKey priv,
Multihash publisher,
byte[] publishValue,
long sequence,
LocalDateTime expiry,
long ttlNanos,
public CompletableFuture<Void> publishValue(Multihash publisher,
byte[] signedRecord,
Host us) {
Set<Multihash> publishes = Collections.synchronizedSet(new HashSet<>());
int minPublishes = 20;
Expand Down Expand Up @@ -370,7 +362,7 @@ public CompletableFuture<Void> 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;
Expand All @@ -396,7 +388,7 @@ public CompletableFuture<Void> 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);
});
})
Expand Down
25 changes: 0 additions & 25 deletions src/main/java/org/peergos/protocol/dht/KademliaController.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,31 +47,6 @@ default CompletableFuture<Providers> getProviders(Multihash block) {
.thenApply(Providers::fromProtobuf);
}

default CompletableFuture<Boolean> 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<Boolean> putValue(Multihash peerId, byte[] value) {
byte[] ipnsRecordKey = IPNS.getKey(peerId);
Dht.Message outgoing = Dht.Message.newBuilder()
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/org/peergos/protocol/ipns/IPNS.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/org/peergos/EmbeddedIpfsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand All @@ -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.*;
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/peergos/IpnsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 457c7a6

Please sign in to comment.