Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Break up the protocol file into individual files. #23

Merged
merged 3 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
541 changes: 0 additions & 541 deletions uma/protocol.go

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uma
package protocol

type CounterPartyDataOption struct {
Mandatory bool `json:"mandatory"`
Expand Down
2 changes: 1 addition & 1 deletion uma/currency.go → uma/protocol/currency.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uma
package protocol

type Currency struct {
// Code is the ISO 4217 (if applicable) currency code (eg. "USD"). For cryptocurrencies, this will be a ticker
Expand Down
2 changes: 1 addition & 1 deletion uma/kyc_status.go → uma/protocol/kyc_status.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uma
package protocol

import "encoding/json"

Expand Down
111 changes: 111 additions & 0 deletions uma/protocol/lnurl_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package protocol

import (
"errors"
"fmt"
"github.com/uma-universal-money-address/uma-go-sdk/uma/utils"
"net/url"
"strconv"
"strings"
"time"
)

// LnurlpRequest is the first request in the UMA protocol.
// It is sent by the VASP that is sending the payment to find out information about the receiver.
type LnurlpRequest struct {
// ReceiverAddress is the address of the user at VASP2 that is receiving the payment.
ReceiverAddress string
// Nonce is a random string that is used to prevent replay attacks.
Nonce *string
// Signature is the base64-encoded signature of sha256(ReceiverAddress|Nonce|Timestamp).
Signature *string
// IsSubjectToTravelRule indicates VASP1 is a financial institution that requires travel rule information.
IsSubjectToTravelRule *bool
// VaspDomain is the domain of the VASP that is sending the payment. It will be used by VASP2 to fetch the public keys of VASP1.
VaspDomain *string
// Timestamp is the unix timestamp of when the request was sent. Used in the signature.
Timestamp *time.Time
// UmaVersion is the version of the UMA protocol that VASP1 prefers to use for this transaction. For the version
// negotiation flow, see https://static.swimlanes.io/87f5d188e080cb8e0494e46f80f2ae74.png
UmaVersion *string
}

// AsUmaRequest returns the request as an UmaLnurlpRequest if it is a valid UMA request, otherwise it returns nil.
// This is useful for validation and avoiding nil pointer dereferences.
func (q *LnurlpRequest) AsUmaRequest() *UmaLnurlpRequest {
if !q.IsUmaRequest() {
return nil
}
return &UmaLnurlpRequest{
LnurlpRequest: *q,
ReceiverAddress: q.ReceiverAddress,
Nonce: *q.Nonce,
Signature: *q.Signature,
IsSubjectToTravelRule: q.IsSubjectToTravelRule != nil && *q.IsSubjectToTravelRule,
VaspDomain: *q.VaspDomain,
Timestamp: *q.Timestamp,
UmaVersion: *q.UmaVersion,
}
}

// IsUmaRequest returns true if the request is a valid UMA request, otherwise, if any fields are missing, it returns false.
func (q *LnurlpRequest) IsUmaRequest() bool {
return q.VaspDomain != nil && q.Nonce != nil && q.Signature != nil && q.Timestamp != nil && q.UmaVersion != nil
}

func (q *LnurlpRequest) EncodeToUrl() (*url.URL, error) {
receiverAddressParts := strings.Split(q.ReceiverAddress, "@")
if len(receiverAddressParts) != 2 {
return nil, errors.New("invalid receiver address")
}
scheme := "https"
if utils.IsDomainLocalhost(receiverAddressParts[1]) {
scheme = "http"
}
lnurlpUrl := url.URL{
Scheme: scheme,
Host: receiverAddressParts[1],
Path: fmt.Sprintf("/.well-known/lnurlp/%s", receiverAddressParts[0]),
}
queryParams := lnurlpUrl.Query()
if q.IsUmaRequest() {
queryParams.Add("signature", *q.Signature)
queryParams.Add("vaspDomain", *q.VaspDomain)
queryParams.Add("nonce", *q.Nonce)
isSubjectToTravelRule := *q.IsSubjectToTravelRule
queryParams.Add("isSubjectToTravelRule", strconv.FormatBool(isSubjectToTravelRule))
queryParams.Add("timestamp", strconv.FormatInt(q.Timestamp.Unix(), 10))
queryParams.Add("umaVersion", *q.UmaVersion)
}
lnurlpUrl.RawQuery = queryParams.Encode()
return &lnurlpUrl, nil
}

// UmaLnurlpRequest is the first request in the UMA protocol.
// It is sent by the VASP that is sending the payment to find out information about the receiver.
type UmaLnurlpRequest struct {
LnurlpRequest
// ReceiverAddress is the address of the user at VASP2 that is receiving the payment.
ReceiverAddress string
// Nonce is a random string that is used to prevent replay attacks.
Nonce string
// Signature is the base64-encoded signature of sha256(ReceiverAddress|Nonce|Timestamp).
Signature string
// IsSubjectToTravelRule indicates VASP1 is a financial institution that requires travel rule information.
IsSubjectToTravelRule bool
// VaspDomain is the domain of the VASP that is sending the payment. It will be used by VASP2 to fetch the public keys of VASP1.
VaspDomain string
// Timestamp is the unix timestamp of when the request was sent. Used in the signature.
Timestamp time.Time
// UmaVersion is the version of the UMA protocol that VASP1 prefers to use for this transaction. For the version
// negotiation flow, see https://static.swimlanes.io/87f5d188e080cb8e0494e46f80f2ae74.png
UmaVersion string
}

func (q *LnurlpRequest) SignablePayload() ([]byte, error) {
if q.Timestamp == nil || q.Nonce == nil {
return nil, errors.New("timestamp and nonce are required for signing")
}
payloadString := strings.Join([]string{q.ReceiverAddress, *q.Nonce, strconv.FormatInt(q.Timestamp.Unix(), 10)}, "|")
return []byte(payloadString), nil
}
101 changes: 101 additions & 0 deletions uma/protocol/lnurl_response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package protocol

import (
"strconv"
"strings"
)

// LnurlpResponse is the response to the LnurlpRequest.
// It is sent by the VASP that is receiving the payment to provide information to the sender about the receiver.
type LnurlpResponse struct {
Tag string `json:"tag"`
Callback string `json:"callback"`
MinSendable int64 `json:"minSendable"`
MaxSendable int64 `json:"maxSendable"`
EncodedMetadata string `json:"metadata"`
// Currencies is the list of currencies that the receiver can quote. See LUD-21. Required for UMA.
Currencies *[]Currency `json:"currencies"`
// RequiredPayerData the data about the payer that the sending VASP must provide in order to send a payment.
RequiredPayerData *CounterPartyDataOptions `json:"payerData"`
// Compliance is compliance-related data from the receiving VASP for UMA.
Compliance *LnurlComplianceResponse `json:"compliance"`
// UmaVersion is the version of the UMA protocol that VASP2 has chosen for this transaction based on its own support
// and VASP1's specified preference in the LnurlpRequest. For the version negotiation flow, see
// https://static.swimlanes.io/87f5d188e080cb8e0494e46f80f2ae74.png
UmaVersion *string `json:"umaVersion"`
// CommentCharsAllowed is the number of characters that the sender can include in the comment field of the pay request.
CommentCharsAllowed *int `json:"commentAllowed"`
// NostrPubkey is an optional nostr pubkey used for nostr zaps (NIP-57). If set, it should be a valid BIP-340 public
// key in hex format.
NostrPubkey *string `json:"nostrPubkey"`
// AllowsNostr should be set to true if the receiving VASP allows nostr zaps (NIP-57).
AllowsNostr *bool `json:"allowsNostr"`
}

// LnurlComplianceResponse is the `compliance` field of the LnurlpResponse.
type LnurlComplianceResponse struct {
// KycStatus indicates whether VASP2 has KYC information about the receiver.
KycStatus KycStatus `json:"kycStatus"`
// Signature is the base64-encoded signature of sha256(ReceiverAddress|Nonce|Timestamp).
Signature string `json:"signature"`
// Nonce is a random string that is used to prevent replay attacks.
Nonce string `json:"signatureNonce"`
// Timestamp is the unix timestamp of when the request was sent. Used in the signature.
Timestamp int64 `json:"signatureTimestamp"`
// IsSubjectToTravelRule indicates whether VASP2 is a financial institution that requires travel rule information.
IsSubjectToTravelRule bool `json:"isSubjectToTravelRule"`
// ReceiverIdentifier is the identifier of the receiver at VASP2.
ReceiverIdentifier string `json:"receiverIdentifier"`
}

func (r *LnurlpResponse) IsUmaResponse() bool {
return r.Compliance != nil && r.UmaVersion != nil && r.Currencies != nil && r.RequiredPayerData != nil
}

func (r *LnurlpResponse) AsUmaResponse() *UmaLnurlpResponse {
if !r.IsUmaResponse() {
return nil
}
return &UmaLnurlpResponse{
LnurlpResponse: *r,
Currencies: *r.Currencies,
RequiredPayerData: *r.RequiredPayerData,
Compliance: *r.Compliance,
UmaVersion: *r.UmaVersion,
CommentCharsAllowed: r.CommentCharsAllowed,
NostrPubkey: r.NostrPubkey,
AllowsNostr: r.AllowsNostr,
}
}

// UmaLnurlpResponse is the UMA response to the LnurlpRequest.
// It is sent by the VASP that is receiving the payment to provide information to the sender about the receiver.
type UmaLnurlpResponse struct {
LnurlpResponse
// Currencies is the list of currencies that the receiver can quote. See LUD-21. Required for UMA.
Currencies []Currency `json:"currencies"`
// RequiredPayerData the data about the payer that the sending VASP must provide in order to send a payment.
RequiredPayerData CounterPartyDataOptions `json:"payerData"`
// Compliance is compliance-related data from the receiving VASP for UMA.
Compliance LnurlComplianceResponse `json:"compliance"`
// UmaVersion is the version of the UMA protocol that VASP2 has chosen for this transaction based on its own support
// and VASP1's specified preference in the LnurlpRequest. For the version negotiation flow, see
// https://static.swimlanes.io/87f5d188e080cb8e0494e46f80f2ae74.png
UmaVersion string `json:"umaVersion"`
// CommentCharsAllowed is the number of characters that the sender can include in the comment field of the pay request.
CommentCharsAllowed *int `json:"commentAllowed"`
// NostrPubkey is an optional nostr pubkey used for nostr zaps (NIP-57). If set, it should be a valid BIP-340 public
// key in hex format.
NostrPubkey *string `json:"nostrPubkey"`
// AllowsNostr should be set to true if the receiving VASP allows nostr zaps (NIP-57).
AllowsNostr *bool `json:"allowsNostr"`
}

func (r *UmaLnurlpResponse) SignablePayload() []byte {
payloadString := strings.Join([]string{
r.Compliance.ReceiverIdentifier,
r.Compliance.Nonce,
strconv.FormatInt(r.Compliance.Timestamp, 10),
}, "|")
return []byte(payloadString)
}
Loading
Loading