Skip to content
This repository has been archived by the owner on Jun 1, 2022. It is now read-only.

Commit

Permalink
Introduce Local SPSP Stream Receiver Link (#426)
Browse files Browse the repository at this point in the history
* Fixes 415 to add support for an in-memory Stateless Stream Receiver Link. This enables a Connector to fulfill ILP packets without having to forward to an actual SPSP Receiver server.
* Fix javadoc
* Add ping packet to emitters
* Update library current version
* Fix unit tests

Signed-off-by: David Fuelling <sappenin@gmail.com>
  • Loading branch information
sappenin committed Jan 31, 2020
1 parent 16f1781 commit a161f29
Show file tree
Hide file tree
Showing 11 changed files with 525 additions and 4 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Modules in this library can be included in your Maven project by adding the Mave
<dependency>
<groupId>org.interledger</groupId>
<artifactId>ilp-core</artifactId>
<version>1.0.2</version>
<version>1.0.3</version>
</dependency>
...
</dependencies>
Expand All @@ -120,7 +120,7 @@ Modules in this library can be included in your Gradle project by adding the Mav
```
dependencies {
...
compile group: 'org.interledger', name: 'ilp-core', version: '1.0.2'
compile group: 'org.interledger', name: 'ilp-core', version: '1.0.3'
...
}
```
Expand Down
4 changes: 4 additions & 0 deletions examples-parent/ilp-emitters/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
<groupId>org.interledger</groupId>
<artifactId>codecs-ildcp</artifactId>
</dependency>
<dependency>
<groupId>org.interledger</groupId>
<artifactId>link-core</artifactId>
</dependency>
<dependency>
<groupId>org.interledger</groupId>
<artifactId>stream-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.interledger.core.InterledgerPreparePacketBuilder;
import org.interledger.core.InterledgerRejectPacket;
import org.interledger.core.SharedSecret;
import org.interledger.link.PingLoopbackLink;
import org.interledger.stream.Denomination;
import org.interledger.stream.StreamPacket;
import org.interledger.stream.crypto.JavaxStreamEncryptionService;
Expand Down Expand Up @@ -50,9 +51,11 @@ public class IlpPacketEmitter {

private static final Logger LOGGER = LoggerFactory.getLogger(IlpPacketEmitter.class);

private static final InterledgerAddress PING_DESTINATION_ADDRESS = InterledgerAddress.of("test.connie");

private static final InterledgerAddress DESTINATION_ADDRESS = InterledgerAddress
.of("example.connie.bob.QeJvQtFp7eRiNhnoAg9PkusR");
private static final InterledgerAddress OPERATOR_ADDRESS = InterledgerAddress.of("example.connie");
.of("test.connie.bob.QeJvQtFp7eRiNhnoAg9PkusR");
private static final InterledgerAddress OPERATOR_ADDRESS = InterledgerAddress.of("test.connie");

private static final SharedSecret SHARED_SECRET = SharedSecret
.of(Base64.getDecoder().decode("nHYRcu5KM5pyw8XehssZtvhEgCgkKP4Do5kJUpk84G4"));
Expand All @@ -62,6 +65,7 @@ public class IlpPacketEmitter {
public static void main(String[] args) throws IOException {
emitPacketsWithNoData();
emitPacketsWithStreamPayloads();
emitUnidrectionalPingPacket();
}

private static void emitPacketsWithNoData() {
Expand All @@ -81,6 +85,18 @@ private static void emitPacketsWithNoData() {
emitPacketToFile("/tmp/testFulfillPacket.bin", fulfillPacket);
}

private static void emitUnidrectionalPingPacket() {

final InterledgerPreparePacket preparePacket = InterledgerPreparePacket.builder()
.destination(PING_DESTINATION_ADDRESS)
.expiresAt(Instant.now().plus(365, ChronoUnit.DAYS))
.executionCondition(PingLoopbackLink.PING_PROTOCOL_CONDITION)
.amount(UnsignedLong.ONE)
.build();

emitPacketToFile("/tmp/testUnidirectionalPingPacket.bin", preparePacket);
}

private static void emitPacketsWithStreamPayloads() throws IOException {
final InterledgerPreparePacket preparePacketWithStreamFrames = preparePacketWithStreamFrames().build();
emitPacketToFile("/tmp/testPreparePacketWithStreamFrames.bin", preparePacketWithStreamFrames);
Expand Down
80 changes: 80 additions & 0 deletions link-parent/link-stateless-spsp-receiver/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.interledger</groupId>
<artifactId>link-parent</artifactId>
<version>HEAD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<name>Quilt :: Link :: Stateless SPSP Receiver</name>
<artifactId>link-stateless-spsp-receiver</artifactId>
<description>A Link implementation for a stateless SPSP receiver.</description>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>codecs-framework</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>ilp-core</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>link-core</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>stream-receiver</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>problem</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.interledger.link.spsp;

import org.interledger.core.InterledgerAddress;
import org.interledger.core.InterledgerPreparePacket;
import org.interledger.core.InterledgerResponsePacket;
import org.interledger.link.AbstractLink;
import org.interledger.link.Link;
import org.interledger.link.LinkHandler;
import org.interledger.link.LinkType;
import org.interledger.link.exceptions.LinkHandlerAlreadyRegisteredException;
import org.interledger.stream.Denomination;
import org.interledger.stream.receiver.StreamReceiver;

import java.util.Objects;
import java.util.function.Supplier;

/**
* <p>A {@link Link} that attempts to fulfill packets using an SPSP receiver.</p>
*/
public class StatelessSpspReceiverLink extends AbstractLink<StatelessSpspReceiverLinkSettings>
implements Link<StatelessSpspReceiverLinkSettings> {

public static final String LINK_TYPE_STRING = "STATELESS_SPSP_RECEIVER";
public static final LinkType LINK_TYPE = LinkType.of(LINK_TYPE_STRING);

private final StreamReceiver streamReceiver;
private final Denomination denomination;

/**
* Required-Args Constructor.
*
* @param operatorAddressSupplier A supplier for the ILP address of this node operating this Link. This value may be
* uninitialized, for example, in cases where the Link obtains its address from a
* parent node using IL-DCP. If an ILP address has not been assigned, or it has not
* been obtained via IL-DCP, then this value will by default be {@link Link#SELF}.
* @param linkSettings A {@link StatelessSpspReceiverLinkSettings} for this Link.
* @param streamReceiver A {@link StreamReceiver} that can fulfill packets.
*/
public StatelessSpspReceiverLink(
final Supplier<InterledgerAddress> operatorAddressSupplier,
final StatelessSpspReceiverLinkSettings linkSettings,
final StreamReceiver streamReceiver
) {
super(operatorAddressSupplier, linkSettings);
this.denomination = Denomination.builder()
.assetCode(linkSettings.assetCode())
.assetScale((short) linkSettings.assetScale())
.build();
this.streamReceiver = Objects.requireNonNull(streamReceiver);
}

@Override
public void registerLinkHandler(final LinkHandler ilpDataHandler) throws LinkHandlerAlreadyRegisteredException {
throw new RuntimeException(
"StatelessSpspReceiver links never emit data, and thus should not have a registered DataHandler."
);
}

@Override
public InterledgerResponsePacket sendPacket(final InterledgerPreparePacket preparePacket) {
Objects.requireNonNull(preparePacket, "preparePacket must not be null");

return streamReceiver.receiveMoney(preparePacket, this.getOperatorAddressSupplier().get(), this.denomination)
.map(fulfillPacket -> {
if (logger.isDebugEnabled()) {
logger.debug("Packet fulfilled! preparePacket={} fulfillPacket={}", preparePacket, fulfillPacket);
}
return fulfillPacket;
},
rejectPacket -> {
if (logger.isDebugEnabled()) {
logger.debug("Packet rejected! preparePacket={} rejectPacket={}", preparePacket, rejectPacket);
}
return rejectPacket;
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.interledger.link.spsp;

import org.interledger.core.InterledgerAddress;
import org.interledger.link.Link;
import org.interledger.link.LinkFactory;
import org.interledger.link.LinkId;
import org.interledger.link.LinkSettings;
import org.interledger.link.LinkType;
import org.interledger.link.PacketRejector;
import org.interledger.link.exceptions.LinkException;
import org.interledger.stream.receiver.StatelessStreamReceiver;

import com.google.common.base.Preconditions;

import java.util.Objects;
import java.util.function.Supplier;

/**
* An implementation of {@link LinkFactory} for constructing instances of {@link StatelessSpspReceiverLink}.
*/
public class StatelessSpspReceiverLinkFactory implements LinkFactory {

private final PacketRejector packetRejector;
private final StatelessStreamReceiver statelessStreamReceiver;

/**
* Required-args Constructor.
*
* @param packetRejector An instance of {@link PacketRejector}.
* @param statelessStreamReceiver A {@link StatelessStreamReceiver} for encrypting/decrypting STREAM packets.
*/
public StatelessSpspReceiverLinkFactory(
final PacketRejector packetRejector, final StatelessStreamReceiver statelessStreamReceiver
) {
this.packetRejector = Objects.requireNonNull(packetRejector, "packetRejector must not be null");
this.statelessStreamReceiver = Objects
.requireNonNull(statelessStreamReceiver, "statelessStreamReceiver must not be null");
}


@Override
public Link<?> constructLink(
final Supplier<InterledgerAddress> operatorAddressSupplier, final LinkSettings linkSettings
) {
Objects.requireNonNull(operatorAddressSupplier, "operatorAddressSupplier must not be null");
Objects.requireNonNull(linkSettings, "linkSettings must not be null");

if (!this.supports(linkSettings.getLinkType())) {
throw new LinkException(
String.format("LinkType not supported by this factory. linkType=%s", linkSettings.getLinkType()),
LinkId.of("n/a")
);
}

Preconditions.checkArgument(
StatelessSpspReceiverLinkSettings.class.isAssignableFrom(linkSettings.getClass()),
"Constructing an instance of StatelessSpspReceiverLink requires an instance of StatelessSpspReceiverLinkSettings"
);

return new StatelessSpspReceiverLink(
operatorAddressSupplier, (StatelessSpspReceiverLinkSettings) linkSettings, statelessStreamReceiver
);
}

@Override
public boolean supports(LinkType linkType) {
return StatelessSpspReceiverLink.LINK_TYPE.equals(linkType);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.interledger.link.spsp;

import org.interledger.link.LinkSettings;
import org.interledger.link.LinkType;

import org.immutables.value.Value;
import org.immutables.value.Value.Derived;

import java.util.Map;
import java.util.Objects;

/**
* An extension of {@link LinkSettings} for Stateless SPSP receiver links.
*/
public interface StatelessSpspReceiverLinkSettings extends LinkSettings {

static ImmutableStatelessSpspReceiverLinkSettings.Builder builder() {
return ImmutableStatelessSpspReceiverLinkSettings.builder();
}

@Override
default LinkType getLinkType() {
return StatelessSpspReceiverLink.LINK_TYPE;
}

/**
* Currency code or other asset identifier that will be used to select the correct rate for this account.
*/
String assetCode();

/**
* Interledger amounts are integers, but most currencies are typically represented as # fractional units, e.g. cents.
* This property defines how many Interledger units make # up one regular unit. For dollars, this would usually be set
* to 9, so that Interledger # amounts are expressed in nano-dollars.
*
* @return an int representing this account's asset scale.
*/
int assetScale();

@Value.Immutable
abstract class AbstractStatelessSpspReceiverLinkSettings implements StatelessSpspReceiverLinkSettings {

@Derived
@Override
public LinkType getLinkType() {
return StatelessSpspReceiverLink.LINK_TYPE;
}

}
}
Loading

0 comments on commit a161f29

Please sign in to comment.