Skip to content
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: 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"

}