Skip to content

Commit 56d7b2f

Browse files
committed
Add demo of transfer between users
1 parent 730ce1c commit 56d7b2f

File tree

2 files changed

+114
-10
lines changed

2 files changed

+114
-10
lines changed

samples/src/main/java/jvmMain/kotlin/Sample.kt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import net.i2p.crypto.eddsa.EdDSAPrivateKey
77
import net.i2p.crypto.eddsa.EdDSAPublicKey
88
import one.mixin.bot.HttpClient
99
import one.mixin.bot.SessionToken
10+
import one.mixin.bot.api.SnapshotService
1011
import one.mixin.bot.encryptPin
1112
import one.mixin.bot.extension.base64Decode
1213
import one.mixin.bot.extension.base64Encode
@@ -15,6 +16,7 @@ import one.mixin.bot.util.decryASEKey
1516
import one.mixin.bot.util.generateEd25519KeyPair
1617
import one.mixin.bot.util.getEdDSAPrivateKeyFromString
1718
import one.mixin.bot.vo.*
19+
import retrofit2.http.Query
1820
import java.util.Random
1921
import java.util.UUID
2022

@@ -100,7 +102,7 @@ fun main() = runBlocking {
100102
return@runBlocking
101103
}
102104

103-
private suspend fun createUser(client: HttpClient, sessionSecret: String): User? {
105+
internal suspend fun createUser(client: HttpClient, sessionSecret: String): User? {
104106
val response = client.userService.createUsers(
105107
AccountRequest(
106108
Random().nextInt(10).toString() + "User",
@@ -110,7 +112,7 @@ private suspend fun createUser(client: HttpClient, sessionSecret: String): User?
110112
return response.data
111113
}
112114

113-
private suspend fun createPin(client: HttpClient, userAesKey: String) {
115+
internal suspend fun createPin(client: HttpClient, userAesKey: String) {
114116
val response = client.userService.createPin(
115117
PinRequest(encryptPin(userAesKey, DEFAULT_PIN))
116118
)
@@ -121,25 +123,28 @@ private suspend fun createPin(client: HttpClient, userAesKey: String) {
121123
}
122124
}
123125

124-
private suspend fun transferToUser(
126+
internal suspend fun transferToUser(
125127
client: HttpClient,
126128
userId: String,
127129
aseKey: String,
128130
pin: String
129-
) {
131+
): Snapshot? {
130132
val response = client.snapshotService.transfer(
131133
TransferRequest(
132134
CNB_ID,
133135
userId,
134136
DEFAULT_AMOUNT,
135-
encryptPin(aseKey, pin, System.nanoTime())
137+
encryptPin(aseKey, pin)
136138
)
137139
)
140+
var snapshot: Snapshot? = null
138141
if (response.isSuccess()) {
142+
snapshot = response.data
139143
println("Transfer success: ${response.data?.snapshotId}")
140144
} else {
141145
println("Transfer failure ${response.error}")
142146
}
147+
return snapshot
143148
}
144149

145150
private suspend fun getAsset(client: HttpClient) {
@@ -274,7 +279,7 @@ private suspend fun transactions(
274279
}
275280
}
276281

277-
private suspend fun networkSnapshot(
282+
internal suspend fun networkSnapshot(
278283
client: HttpClient,
279284
snapshotId: String
280285
) {
@@ -286,16 +291,22 @@ private suspend fun networkSnapshot(
286291
}
287292
}
288293

289-
private suspend fun networkSnapshots(
294+
internal suspend fun networkSnapshots(
290295
client: HttpClient,
291-
assetId: String
292-
) {
293-
val snapshotResponse = client.snapshotService.networkSnapshots(assetId)
296+
assetId: String,
297+
offset: String? = null,
298+
limit: Int = SnapshotService.LIMIT,
299+
order: String? = null
300+
): List<NetworkSnapshot>? {
301+
val snapshotResponse = client.snapshotService.networkSnapshots(assetId, offset, limit, order)
302+
var networkSnapshots: List<NetworkSnapshot>? = null
294303
if (snapshotResponse.isSuccess()) {
304+
networkSnapshots = snapshotResponse.data
295305
println("Network snapshots success: ${snapshotResponse.data?.size}")
296306
} else {
297307
println("Network snapshot failure: ${snapshotResponse.error?.description}")
298308
}
309+
return networkSnapshots
299310
}
300311

301312
private suspend fun readGhostKey(client: HttpClient) {
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package jvmMain.kotlin
2+
3+
import kotlinx.coroutines.delay
4+
import kotlinx.coroutines.runBlocking
5+
import net.i2p.crypto.eddsa.EdDSAPrivateKey
6+
import net.i2p.crypto.eddsa.EdDSAPublicKey
7+
import one.mixin.bot.HttpClient
8+
import one.mixin.bot.SessionToken
9+
import one.mixin.bot.extension.base64Decode
10+
import one.mixin.bot.extension.base64Encode
11+
import one.mixin.bot.util.calculateAgreement
12+
import one.mixin.bot.util.decryASEKey
13+
import one.mixin.bot.util.generateEd25519KeyPair
14+
import one.mixin.bot.util.getEdDSAPrivateKeyFromString
15+
16+
fun main() = runBlocking {
17+
val key = getEdDSAPrivateKeyFromString(Config.privateKey)
18+
val pinToken = decryASEKey(Config.pinTokenPem, key) ?: return@runBlocking
19+
val client =
20+
HttpClient.Builder().useCNServer().configEdDSA(Config.userId, Config.sessionId, key).build()
21+
22+
// create alice keys
23+
val aliceSessionKey = generateEd25519KeyPair()
24+
val alicePublicKey = aliceSessionKey.public as EdDSAPublicKey
25+
val aliceSessionSecret = alicePublicKey.abyte.base64Encode()
26+
27+
// create alice
28+
val alice = createUser(client, aliceSessionSecret)
29+
alice ?: return@runBlocking
30+
val aliceToken = SessionToken.EdDSA(
31+
alice.userId, alice.sessionId,
32+
(aliceSessionKey.private as EdDSAPrivateKey).seed.base64Encode()
33+
)
34+
client.setUserToken(aliceToken)
35+
// decrypt pin token
36+
val alicePrivateKey = aliceSessionKey.private as EdDSAPrivateKey
37+
val aliceAesKey = calculateAgreement(alice.pinToken.base64Decode(), alicePrivateKey).base64Encode()
38+
// create alice's pin
39+
createPin(client, aliceAesKey)
40+
41+
// use bot's token
42+
client.setUserToken(null)
43+
// create bob keys
44+
val bobSessionKey = generateEd25519KeyPair()
45+
val bobPublicKey = bobSessionKey.public as EdDSAPublicKey
46+
val bobSessionSecret = bobPublicKey.abyte.base64Encode()
47+
48+
// create bob
49+
val bob = createUser(client, bobSessionSecret)
50+
bob ?: return@runBlocking
51+
val bobToken = SessionToken.EdDSA(
52+
bob.userId, bob.sessionId,
53+
(bobSessionKey.private as EdDSAPrivateKey).seed.base64Encode()
54+
)
55+
client.setUserToken(bobToken)
56+
// decrypt pin token
57+
val bobPrivateKey = bobSessionKey.private as EdDSAPrivateKey
58+
val bobAesKey = calculateAgreement(bob.pinToken.base64Decode(), bobPrivateKey).base64Encode()
59+
// create bob's pin
60+
createPin(client, bobAesKey)
61+
62+
// use bot's token
63+
client.setUserToken(null)
64+
// bot transfer to alice
65+
val snapshotBot2Alice = transferToUser(client, alice.userId, pinToken, Config.pin)
66+
67+
delay(4000)
68+
69+
// use alice's token
70+
client.setUserToken(aliceToken)
71+
if (snapshotBot2Alice != null) {
72+
// alice check transfer
73+
networkSnapshot(client, snapshotBot2Alice.snapshotId)
74+
75+
val aliceNetworkSnapshots = networkSnapshots(client, CNB_ID, limit = 5)
76+
assert(aliceNetworkSnapshots?.find { it.snapshotId == snapshotBot2Alice.snapshotId } != null)
77+
}
78+
79+
// alice transfer to bob
80+
val snapshotAlice2Bob = transferToUser(client, bob.userId, aliceAesKey, DEFAULT_PIN)
81+
82+
delay(4000)
83+
84+
// use bob's token
85+
client.setUserToken(bobToken)
86+
if (snapshotAlice2Bob != null) {
87+
networkSnapshot(client, snapshotAlice2Bob.snapshotId)
88+
89+
val bobNetworkSnapshots = networkSnapshots(client, CNB_ID, limit = 5)
90+
assert(bobNetworkSnapshots?.find { it.snapshotId == snapshotAlice2Bob.snapshotId } != null)
91+
}
92+
93+
}

0 commit comments

Comments
 (0)