Skip to content

Commit

Permalink
support config client with access token (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
boyan01 authored Mar 5, 2024
1 parent e909dc9 commit e3efc8a
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 37 deletions.
1 change: 1 addition & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ configurations {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.okhttp3:logging-interceptor:$okhttpVersion"
implementation "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
Expand Down
28 changes: 18 additions & 10 deletions library/src/main/kotlin/one/mixin/bot/HttpClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,21 @@ import java.security.Security

@Suppress("unused")
class HttpClient private constructor(
val safeUser: SafeUser,
val safeUser: SafeUser?,
private val accessToken: String? = null,
debug: Boolean = false,
) {
init {
Security.addProvider(BouncyCastleProvider())
}

private val okHttpClient: OkHttpClient by lazy {
createHttpClient(safeUser, false, debug)
createHttpClient(safeUser, accessToken, false, debug)
}

private val retrofit: Retrofit by lazy {
val builder = Retrofit.Builder()
.baseUrl(URL)
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
val builder = Retrofit.Builder().baseUrl(URL).addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create()).client(okHttpClient)
builder.build()
}

Expand Down Expand Up @@ -91,8 +89,9 @@ class HttpClient private constructor(
}

class Builder {
private lateinit var safeUser: SafeUser
private var safeUser: SafeUser? = null
private var debug: Boolean = false
private var accessToken: String? = null

fun configSafeUser(
userId: String,
Expand All @@ -101,7 +100,13 @@ class HttpClient private constructor(
serverPublicKey: ByteArray? = null,
spendPrivateKey: ByteArray? = null,
): Builder {
safeUser = SafeUser(userId, sessionId, sessionPrivateKey.sliceArray(0..31), serverPublicKey, spendPrivateKey)
safeUser =
SafeUser(userId, sessionId, sessionPrivateKey.sliceArray(0..31), serverPublicKey, spendPrivateKey)
return this
}

fun configAccessToken(accessToken: String): Builder {
this.accessToken = accessToken
return this
}

Expand All @@ -111,7 +116,10 @@ class HttpClient private constructor(
}

fun build(): HttpClient {
return HttpClient(safeUser, debug)
require(!(safeUser == null && accessToken == null)) { "safeUser and accessToken can't be null at the same time" }
return HttpClient(safeUser, accessToken, debug)
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import retrofit2.http.Query
interface UtxoCallService {
@GET("safe/outputs")
fun getOutputsCall(
@Query("members") members: String,
@Query("members") members: String?,
@Query("threshold") threshold: Int,
@Query("offset") offset: Long? = null,
@Query("limit") limit: Int = 500,
Expand Down
23 changes: 16 additions & 7 deletions library/src/main/kotlin/one/mixin/bot/blaze/BlazeClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class BlazeClient private constructor(
private var reconnectInterval = 5000

private val okHttpClient: OkHttpClient by lazy {
createHttpClient(safeUser, true, debug)
createHttpClient(safeUser, null, true, debug)
}

private var webSocket: WebSocket? = null
Expand Down Expand Up @@ -80,7 +80,8 @@ class BlazeClient private constructor(
serverPublicKey: ByteArray? = null,
spendPrivateKey: ByteArray? = null,
): Builder {
safeUser = SafeUser(userId, sessionId, sessionPrivateKey.sliceArray(0..31), serverPublicKey, spendPrivateKey)
safeUser =
SafeUser(userId, sessionId, sessionPrivateKey.sliceArray(0..31), serverPublicKey, spendPrivateKey)
return this
}

Expand Down Expand Up @@ -124,8 +125,7 @@ class BlazeClient private constructor(
}

override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
try {
// 消息通的时,重置连接次数
try { // 消息通的时,重置连接次数
connectCount = 0

val blazeMsg = decodeAs(bytes, parseData)
Expand Down Expand Up @@ -163,17 +163,26 @@ fun sendMsg(webSocket: WebSocket, action: Action, msgParam: MsgParam?): Boolean
}

fun sendTextMsg(webSocket: WebSocket, conversationId: String, recipientId: String, text: String): Boolean {
val msgParam = MsgParam(UUID.randomUUID().toString(), Category.PLAIN_TEXT.toString(), conversationId, recipientId, text)
val msgParam =
MsgParam(UUID.randomUUID().toString(), Category.PLAIN_TEXT.toString(), conversationId, recipientId, text)
return sendMsg(webSocket, Action.CREATE_MESSAGE, msgParam)
}

fun sendCardMsg(webSocket: WebSocket, conversationId: String, recipientId: String, cards: Cards): Boolean {
val msgParam = MsgParam(UUID.randomUUID().toString(), Category.APP_CARD.toString(), conversationId, recipientId, Gson().toJson(cards))
val msgParam = MsgParam(
UUID.randomUUID().toString(), Category.APP_CARD.toString(), conversationId, recipientId, Gson().toJson(cards)
)
return sendMsg(webSocket, Action.CREATE_MESSAGE, msgParam)
}

fun sendButtonsMsg(webSocket: WebSocket, conversationId: String, recipientId: String, buttons: List<Buttons>): Boolean {
val msgParam = MsgParam(UUID.randomUUID().toString(), Category.APP_BUTTON_GROUP.toString(), conversationId, recipientId, Gson().toJson(buttons))
val msgParam = MsgParam(
UUID.randomUUID().toString(),
Category.APP_BUTTON_GROUP.toString(),
conversationId,
recipientId,
Gson().toJson(buttons)
)
return sendMsg(webSocket, Action.CREATE_MESSAGE, msgParam)
}

Expand Down
7 changes: 4 additions & 3 deletions library/src/main/kotlin/one/mixin/bot/safe/Output.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import java.math.BigDecimal

fun assetBalance(
botClient: HttpClient, assetId: String,
members: List<String> = listOf(botClient.safeUser.userId),
members: List<String> = listOf(),
): String {
val outputs = listUnspentOutputs(
botClient, buildHashMembers(members), 1, assetId
Expand Down Expand Up @@ -37,8 +37,9 @@ fun listOutputs(
offset: Long,
limit: Int,
): List<Output> {
val resp =
botClient.utxoService.getOutputsCall(membersHash, threshold, offset, limit, state, assetId).execute().body()
val resp = botClient.utxoService.getOutputsCall(
membersHash.ifEmpty { null }, threshold, offset, limit, state, assetId
).execute().body()
if (resp == null || !resp.isSuccess()) {
throw SafeException("get safe/outputs ${resp?.error}")
}
Expand Down
10 changes: 8 additions & 2 deletions library/src/main/kotlin/one/mixin/bot/safe/Transaction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ fun sendTransactionToUser(
memo: String?,
traceId: String,
): List<TransactionResponse> {
requireNotNull(botClient.safeUser) { "safe user is null" }
verifyTxId(botClient, traceId) // check assetId is kernel assetId

// get unspent outputs for asset and may throw insufficient outputs error
Expand Down Expand Up @@ -122,6 +123,7 @@ suspend fun withdrawalToAddress(
memo: String? = null,
traceId: String = UUID.randomUUID().toString(),
): List<TransactionResponse> {
requireNotNull(botClient.safeUser) { "safe user is null" }
verifyTxId(botClient, traceId)
val token = botClient.tokenService.getAssetById(assetId).requiredData()
val chain = if (token.assetId == token.chainId) {
Expand Down Expand Up @@ -245,7 +247,7 @@ private suspend fun HttpClient.withdrawalTransaction(
val withdrawalData = requestResponse.first { it.requestId == traceId }
val feeData = requestResponse.first { it.requestId == feeTraceId }

val spendKey = safeUser.spendPrivateKey ?: throw SafeException("spend key is null")
val spendKey = safeUser?.spendPrivateKey ?: throw SafeException("spend key is null")

val signedWithdrawalRaw = withdrawalTx.sign(withdrawalData.views, utxos, spendKey.toHex())
val signedFeeRaw = feeTx.sign(feeData.views, feeUtxos, spendKey.toHex())
Expand Down Expand Up @@ -302,7 +304,7 @@ private suspend fun HttpClient.withdrawalTransaction(
utxoService.transactionRequest(listOf(TransactionRequest(tx.encodeToString(), traceId))).requiredData()
.firstOrNull() ?: throw SafeException("request transaction response data null")

val spendKey = safeUser.spendPrivateKey ?: throw SafeException("spend key is null")
val spendKey = safeUser?.spendPrivateKey ?: throw SafeException("spend key is null")
val signedRaw = tx.sign(verifiedTx.views, utxos, spendKey.toHex())

utxoService.transactions(listOf(TransactionRequest(signedRaw, traceId))).requiredData()
Expand All @@ -313,6 +315,7 @@ private fun requestUnspentOutputsForRecipients(
assetId: String,
amount: String,
): Pair<List<Output>, BigDecimal> {
requireNotNull(botClient.safeUser) { "safe user is null" }
val memberHash = buildHashMembers(listOf(botClient.safeUser.userId))
val outputs = listUnspentOutputs(botClient, memberHash, 1, assetId)
if (outputs.isEmpty()) {
Expand All @@ -333,6 +336,9 @@ private fun requestUnspentOutputsForRecipients(
}

fun buildHashMembers(ids: List<String>): String {
if (ids.isEmpty()) {
return ""
}
return ids.sortedBy { it }.joinToString("").sha3Sum256().joinToString("") { "%02x".format(it) }
}

Expand Down
30 changes: 16 additions & 14 deletions library/src/main/kotlin/one/mixin/bot/util/OkHttpProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ import java.util.*
import java.util.concurrent.TimeUnit

fun createHttpClient(
safeUser: SafeUser,
safeUser: SafeUser?,
accessToken: String?,
websocket: Boolean,
debug: Boolean,
): OkHttpClient {
require(!(safeUser == null && accessToken == null)) { "safeUser and accessToken can't be null at the same time" }

val builder = OkHttpClient.Builder()
if (debug) {
val logging = HttpLoggingInterceptor()
Expand Down Expand Up @@ -52,25 +55,24 @@ fun createHttpClient(
if (websocket) {
requestBuilder.addHeader("Sec-WebSocket-Protocol", "MixinBot-Blaze-1")
}
requestBuilder.addHeader("User-Agent", Constants.UA).addHeader("Accept-Language", Locale.getDefault().language).addHeader(
"Authorization",
"Bearer " +
signToken(
safeUser.userId,
requestBuilder.addHeader("User-Agent", Constants.UA)
.addHeader("Accept-Language", Locale.getDefault().language).addHeader(
"Authorization",
"Bearer " + (accessToken ?: signToken(
safeUser!!.userId,
safeUser.sessionId,
chain.request(),
EdDSAPrivateKey(safeUser.sessionPrivateKey.toByteString()),
),
)
)),
)

val request = requestBuilder.build()

val response =
try {
chain.proceed(request)
} catch (e: Exception) {
throw e
}
val response = try {
chain.proceed(request)
} catch (e: Exception) {
throw e
}

if (!response.isSuccessful) {
val code = response.code
Expand Down
3 changes: 3 additions & 0 deletions samples/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"

implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"

}

0 comments on commit e3efc8a

Please sign in to comment.