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

Add travelRuleFormat field to the payerdata compliance #3

Merged
merged 3 commits into from
Oct 21, 2023
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
1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ class UmaProtocolHelper @JvmOverloads constructor(
* compliance provider, this will be used to pre-screen the sender's UTXOs for compliance purposes.
* @param payerName The name of the sender (optional).
* @param payerEmail The email of the sender (optional).
* @param travelRuleFormat An optional standardized format of the travel rule information (e.g. IVMS). Null
* indicates raw json or a custom format.
* @return The [PayRequest] that should be sent to the receiver.
*/
@JvmOverloads
Expand All @@ -260,6 +262,7 @@ class UmaProtocolHelper @JvmOverloads constructor(
payerNodePubKey: String? = null,
payerName: String? = null,
payerEmail: String? = null,
travelRuleFormat: TravelRuleFormat? = null,
): PayRequest {
val compliancePayerData = getSignedCompliancePayerData(
receiverEncryptionPubKey,
Expand All @@ -270,6 +273,7 @@ class UmaProtocolHelper @JvmOverloads constructor(
payerUtxos,
payerNodePubKey,
utxoCallback,
travelRuleFormat,
)
val payerData = PayerData(
identifier = payerIdentifier,
Expand All @@ -293,6 +297,7 @@ class UmaProtocolHelper @JvmOverloads constructor(
payerUtxos: List<String>?,
payerNodePubKey: String?,
utxoCallback: String,
travelRuleFormat: TravelRuleFormat?,
): CompliancePayerData {
val nonce = generateNonce()
val timestamp = System.currentTimeMillis() / 1000
Expand All @@ -305,6 +310,7 @@ class UmaProtocolHelper @JvmOverloads constructor(
signatureNonce = nonce,
signatureTimestamp = timestamp,
utxoCallback = utxoCallback,
travelRuleFormat = travelRuleFormat,
)
val signablePayload = "$payerIdentifier|$nonce|$timestamp".encodeToByteArray()
val signature = signPayload(signablePayload, sendingVaspPrivateKey)
Expand Down
40 changes: 40 additions & 0 deletions uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package me.uma.protocol

import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encodeToString
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json

/**
Expand Down Expand Up @@ -50,6 +55,8 @@ data class PayerData @JvmOverloads constructor(
* @property signature The signature of the sender on the signable payload.
* @property signatureNonce The nonce used in the signature.
* @property signatureTimestamp The timestamp used in the signature.
* @property travelRuleFormat An optional standardized format of the travel rule information (e.g. IVMS). Null
* indicates raw json or a custom format.
*/
@Serializable
data class CompliancePayerData(
Expand All @@ -61,6 +68,39 @@ data class CompliancePayerData(
val signature: String,
val signatureNonce: String,
val signatureTimestamp: Long,
val travelRuleFormat: TravelRuleFormat? = null,
) {
fun signedWith(signature: String) = copy(signature = signature)
}

/**
* A standardized format of the travel rule information.
*/
@Serializable(with = TravelRuleFormatSerializer::class)
data class TravelRuleFormat(
/** The type of the travel rule format (e.g. IVMS). */
val type: String,
/** The version of the travel rule format (e.g. 1.0). */
val version: String?,
)

/**
* Serializes the TravelRuleFormat to string in the format of "type@version". If there's no version, it will be
* serialized as "type".
*/
class TravelRuleFormatSerializer : KSerializer<TravelRuleFormat> {
override val descriptor = PrimitiveSerialDescriptor("TravelRuleFormat", PrimitiveKind.STRING)

override fun deserialize(decoder: Decoder): TravelRuleFormat {
val value = decoder.decodeString()
if (!value.contains("@")) {
return TravelRuleFormat(value, null)
}
val parts = value.split("@")
return TravelRuleFormat(parts[0], parts.getOrNull(1))
}

override fun serialize(encoder: Encoder, value: TravelRuleFormat) {
encoder.encodeString("${value.type}${value.version?.let { "@$it" } ?: ""}")
}
}
33 changes: 33 additions & 0 deletions uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ package me.uma

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.fail
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import me.uma.crypto.Secp256k1
import me.uma.protocol.KycStatus
import me.uma.protocol.PayerDataOptions
import me.uma.protocol.TravelRuleFormat

@OptIn(ExperimentalCoroutinesApi::class)
class UmaTests {
val keys = Secp256k1.generateKeyPair()

@Test
fun `test serialize PayerDataOptions`() = runTest {
val payerDataOptions = PayerDataOptions(
Expand All @@ -22,4 +28,31 @@ class UmaTests {
Json.decodeFromString(PayerDataOptions.serializer(), json),
)
}

@OptIn(ExperimentalStdlibApi::class)
@Test
fun `test create and parse payreq`() = runTest {
val travelRuleInfo = "travel rule info"
val payreq = UmaProtocolHelper().getPayRequest(
receiverEncryptionPubKey = keys.publicKey,
sendingVaspPrivateKey = keys.privateKey,
currencyCode = "USD",
amount = 100,
payerIdentifier = "[email protected]",
payerKycStatus = KycStatus.VERIFIED,
utxoCallback = "https://example.com/utxo",
travelRuleInfo = "travel rule info",
travelRuleFormat = TravelRuleFormat("someFormat", "1.0"),
)
val json = payreq.toJson()
val decodedPayReq = UmaProtocolHelper().parseAsPayRequest(json)
assertEquals(payreq, decodedPayReq)

val encryptedTravelRuleInfo =
decodedPayReq.payerData.compliance?.travelRuleInfo ?: fail("travel rule info not found")
assertEquals(
travelRuleInfo,
String(Secp256k1.decryptEcies(encryptedTravelRuleInfo.hexToByteArray(), keys.privateKey)),
)
}
}