xpring-eng / hermes-ilp Goto Github PK
View Code? Open in Web Editor NEWAn POC ILP-as-a-Service endpoint that provides STREAM sending and SPSP support via gRPC to Xpring Wallet
An POC ILP-as-a-Service endpoint that provides STREAM sending and SPSP support via gRPC to Xpring Wallet
Currently, when Hermes is instructed to create a new account, it's also creating a new static-route so that the Connector routes packets to the SPSP server.
Instead, we should enable locally-fulfilled packets in the Connector, and stop creating static-routes. The changes introduced here mean that we no longer need to add a static route. Instead, we can configure the SPSP server to answer to all accounts in test.jc.spsp
and as long as it has the same shared-secret as the SPSP server, things should just work.
AccountSettings
objects contain a linkType
, which currently serializes to "linkType": {}
. While the current wallet UI doesn't display the link type, it may be useful for a developer using Hermes to know the link type they are using to talk to a connector.
Task here is probably to add a LinkTypeModule
to the Jackson config.
Currently, we have a lot of duplicate code to deal with REST and GRPC request/response entities. Because the GRPC entities are generated, we are treating REST and GRPC entities as completely separate objects, when in reality they should mirror each other. Because of this, there is a lot of duplicate code and unnecessary overloading in the Hermes services (NewAccountService
etc).
We should consider creating wrapper classes (abstract or interface) that both the REST and GRPC entities inherit from, and try to consolidate our service functionality.
Currently, Hermes is all over the place with how it handles auth tokens with the "Bearer "
prefix.
Especially with the introduction of CookieAuthenticationFilter
and cookie filtering in gRPC, we should standardize how we handle auth tokens with and without a "Bearer "
prefix, and which layers are responsible for adding/deleting the prefix.
In order to provide idempotent/async sendMoney functionality, we will need a data store that is able to keep track of pending/complete payments. We should decide on a data store, either Redis or Postgres. Since this will likely change with any more sophisticated balance tracking/resiliency measures, we should probably just go with what's easiest.
This should just be one table with the following fields (essentially storing a SendMoneyResult
plus or minus some fields):
paymentId
: Some unique identifier which will be passed by the client. Assume UUID for noworiginalAmount
: Amount requested to sendamountDelivered
: Amount that was actually deliveredamountSent
: Amount that was sentamountLeftToSend
: Difference between originalAmount
and amountDelivered
destination
: Payment pointer of the receiverstatus
: either PENDING, COMPLETE, or FAILEDNote that this will partially solve the resiliency issue between client and Hermes, but will NOT solve the case of Hermes disconnecting from the connector.
The connector API to create an access token is POST request with no body. Feign will omit the content-length header since there is no content. This makes GCP load balancer unhappy because content-length header is missing and returns error: Error 411 (Length Required)
Although it's completely valid to use POST without a request body content-length header, there appears no way to configure the GCP load balancer to fix this. As a work-around, we should send an empty request body so that Feign will set a content-length header.
Stacktrace:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.zalando.problem.ThrowableProblem` (no Creators, like default construct, exist): abstract type (need to add/enable type information?)
at [Source: (String)"{"title":"Unauthorized","status":401,"detail":"Authentication failed for principal: null"}"; line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1589)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1055)
at com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer.deserializeFromObject(ThrowableDeserializer.java:72)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3205)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3173)
at org.interledger.link.http.IlpOverHttpLink.parseThrowableProblem(IlpOverHttpLink.java:290)
at org.interledger.link.http.IlpOverHttpLink.sendPacket(IlpOverHttpLink.java:149)
at org.interledger.stream.sender.SimpleStreamSender$SendMoneyAggregator.sendPacketAndCheckForFailure(SimpleStreamSender.java:491)
at org.interledger.stream.sender.SimpleStreamSender$SendMoneyAggregator.preflightCheck(SimpleStreamSender.java:469)
at org.interledger.stream.sender.SimpleStreamSender$SendMoneyAggregator.send(SimpleStreamSender.java:368)
at org.interledger.stream.sender.SimpleStreamSender.sendMoney(SimpleStreamSender.java:224)
at org.interledger.spsp.server.services.SendMoneyService.sendMoney(SendMoneyService.java:83)
at org.interledger.spsp.server.grpc.IlpOverHttpGrpcHandler.sendMoney(IlpOverHttpGrpcHandler.java:37)
at org.interledger.spsp.server.grpc.IlpOverHttpServiceGrpc$MethodHandlers.invoke(IlpOverHttpServiceGrpc.java:235)
at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:172)
at io.grpc.PartialForwardingServerCallListener.onHalfClose(PartialForwardingServerCallListener.java:35)
at io.grpc.ForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:23)
at io.grpc.ForwardingServerCallListener$SimpleForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:40)
at io.grpc.Contexts$ContextualizedServerCallListener.onHalfClose(Contexts.java:86)
at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:331)
at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:814)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
at java.util.concurrent.ThreadPoolExecutor.runWorker
at java.util.concurrent.ThreadPoolExecutor$Worker.run
at java.lang.Thread.run
The base package in Hermes is org.interledger.spsp.server
because we forked Hermes from the SPSP server project.
We should rename this package to org.interledger.hermes
.
We also have an orphan package org.interledger.spsp.stream
, which is never used, so we should get rid of it.
The latest version of the Quilt fixes an error where STREAMs that enounter F02s weren't stopping. We should update to that version.
Per #7 (comment), we should encapsulate some of the logic in both the REST controller and GRPC handler for auth into its own class for better code reuse.
Once we implement #15, we will need an endpoint to get a payment. Endpoint should look something like /payments/{payment-id}
.
Since Hermes just passes auth through to the connector, all payments will be visible to all users. We should consider changing this as Hermes becomes more stateful, but for testnet let's not worry about it.
21:12:04.269 [qtp428061834-18] ERROR o.z.p.spring.common.AdviceTraits - Internal Server Error
java.lang.NullPointerException: null
at org.interledger.spsp.server.controllers.AbstractController.getAuthorization(AbstractController.java:22)
at org.interledger.spsp.server.controllers.BalanceController.getBalance(BalanceController.java:40)
at jdk.internal.reflect.GeneratedMethodAccessor55.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMa...
Currently, Hermes stores the admin auth token for the connector, and uses it in createAccount
and getAccount
. Instead, these credentials should come from the caller (Xpring wallet Express server).
The Hermes Jackson config has a PaymentPointerModule
as well as a serializer and deserializer for PaymentPointer
s. This should really go in Quilt, since PaymentPointer
s are defined there
{"title":"Internal Server Error","status":500,"detail":"PaymentPointers must begin with $"}
Instead, this should be a handled error (HTTP code = 400) with the type something like "https://.../invalid-payment-pointer"
Currently, the following is returned:
{"title":"Internal Server Error","status":500,"detail":"IOException failure calling https://foo.example.com/.well-known/pay"}
In order to allow the Xpring wallet server to create an account that a user can authenticate to with either a JWT or an API key, we need to add two fields to the request payload:
api_key
jwt_info
:{
"subject": ...,
"issuer": ...,
"audience": ...
}
We will also need to change account creation logic in Hermes to include both authentication methods in a user's customSettings
. NOTE: This logic may need to change based on @nhartner's changes to AccountSettings
on the connector to handle multiple auth types/API keys
Currently, hermes looks for a JWT in a cookie first. If present, it always honors that value.
Instead, we need Hermes to invert this precedence and honor the Authorization
header first. If that is missing, Hermes should fallback to looking for a JWT in a cookie.
If an account has a disallowed character, we need to encode the value so that we don't accidentally add things into the ILP address during SPSP resolution. For example, if an account is created with an id of test+david
(e.g., the +
is an illegal character, the account will be created but the routing-table entry will be invalid (hermes will throw the following exception):
19:39:52.480 [qtp428061834-17] WARN o.i.s.s.services.NewAccountService - Failed to create route
java.lang.IllegalArgumentException: The 'test+david' segment has an invalid format
at org.interledger.core.InterledgerAddressPrefix$AbstractInterledgerAddressPrefix.check(InterledgerAddressPrefix.java:244)
at org.interledger.core.InterledgerAddressPrefixBuilder$ImmutableInterledgerAddressPrefix.validate(InterledgerAddressPrefixBuilder.java:193)
at org.interledger.core.InterledgerAddressPrefixBuilder$ImmutableInterledgerAddressPrefix.access$100(InterledgerAddressPrefixBuilder.java:117)
at org.interledger.core.InterledgerAddressPrefixBuilder.build(InterledgerAddressPrefixBuilder.java:99)
at org.interledger.core.InterledgerAddressPrefix.of(InterledgerAddressPrefix.java:70)
at org.interledger.core.InterledgerAddressPrefix.with(InterledgerAddressPrefix.java:152)
at org.interledger.spsp.server.services.NewAccountService.createAccount(NewAccountService.java:68)
at org.interledger.spsp.s...
One thing that is not an issue is dots (.
) (e.g., david.foo
). These work properly.
In our integration tests, we start up a docker image for a connector. As the nightly docker image is no longer updated nightly, the docker image version is currently hardcoded to 0.2.0.
This should be configurable, either in pom.xml
or in a yml file, but probably in pom.xml
Currently in SendMoneyService, the "sender address" is computed to be a somewhat invalid value because Hermes doesn't have sufficient SPSP receiver details to compute this value using the shared-secret in the STREAM.
We should either add this functionality into Hermes, or defer this on grounds that Hermes functionality should be in the Connector, which will have the SPSP server information in the runtime.
For now, this is not an issue because Hermes isn't facilitating bidirectional streams ATM.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.