Skip to content

Commit

Permalink
Use statechain_info to verify transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
ssantos21 committed May 15, 2024
1 parent 4547422 commit e5249e0
Show file tree
Hide file tree
Showing 49 changed files with 645 additions and 308 deletions.
2 changes: 2 additions & 0 deletions clients/kotlin/src/main/kotlin/BroadcastBackupTransaction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class BroadcastBackupTransaction: CliktCommand(help = "Broadcast a backup transa

appContext.sqliteManager.updateWallet(wallet)

completeWithdraw(appContext.clientConfig, coin.statechainId!!, coin.signedStatechainId!!)

val json = buildJsonObject {
put("backupTx", backupTxTxid)
put("cpfpTx", cpfpTxTxid)
Expand Down
15 changes: 7 additions & 8 deletions clients/kotlin/src/main/kotlin/CoinUpdate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -169,19 +169,18 @@ class CoinUpdate() {
}

private suspend fun checkTransfer(coin: Coin, clientConfig: ClientConfig): Boolean {
val url = "${clientConfig.statechainEntity}/transfer/receiver/${coin.statechainId}"

val httpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
if (coin.statechainId == null) {
throw Exception("The coin with the aggregated address ${coin.aggregatedAddress} does not have a statechain ID");
}

val response: TransferReceiverGetResponsePayload = httpClient.get(url).body()
val statechainInfo = getStatechainInfo(clientConfig, coin.statechainId!!) ?: return true

val enclavePublicKey = statechainInfo.enclavePublicKey

httpClient.close()
val isTransferred = !isEnclavePubkeyPartOfCoin(coin, enclavePublicKey)

return response.transferComplete
return isTransferred
}

private fun checkWithdrawal(coin: Coin, clientConfig: ClientConfig, walletNetwork: String): Boolean {
Expand Down
52 changes: 52 additions & 0 deletions clients/kotlin/src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,63 @@ import io.ktor.client.call.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import org.electrumj.ElectrumClient
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

suspend fun getStatechainInfo(clientConfig: ClientConfig, statechainId: String): StatechainInfoResponsePayload? {
val url = "${clientConfig.statechainEntity}/info/statechain/${statechainId}"

val httpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}

val response = httpClient.get(url)

if (response.status == HttpStatusCode.NotFound) {
return null
}

val payload: StatechainInfoResponsePayload = response.body()

httpClient.close()

return payload
}

suspend fun completeWithdraw(clientConfig: ClientConfig, statechainId: String, signedStatechainId: String) {

val withdrawCompletePayload = WithdrawCompletePayload(
statechainId,
signedStatechainId
)

val url = "${clientConfig.statechainEntity}/withdraw/complete"

val httpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}

val response = httpClient.post(url) {
contentType(ContentType.Application.Json)
setBody(withdrawCompletePayload)
}

if (response.status != HttpStatusCode.OK) {
val errorBody: String = response.bodyAsText()
throw Exception("Failed to complete withdraw: HTTP ${response.status} - $errorBody")
}

httpClient.close()
}

fun createActivity(utxo: String, amount: UInt, action: String): Activity {
val date = ZonedDateTime.now() // This will get the current date and time in UTC
val isoString = date.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) // Converts the date to an ISO 8601 string
Expand Down
23 changes: 6 additions & 17 deletions clients/kotlin/src/main/kotlin/TransferReceive.kt
Original file line number Diff line number Diff line change
Expand Up @@ -126,22 +126,6 @@ class TransferReceive: CliktCommand(help = "Retrieve coins from server") {
httpClient.close()
}

private suspend fun getStatechainInfo(statechainId: String): StatechainInfoResponsePayload {
val url = "${appContext.clientConfig.statechainEntity}/info/statechain/${statechainId}"

val httpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}

val response: StatechainInfoResponsePayload = httpClient.get(url).body()

httpClient.close()

return response
}

private fun getTx0(tx0Txid: String) : String {
val electrumClient = getElectrumClient(appContext.clientConfig)

Expand Down Expand Up @@ -176,7 +160,12 @@ class TransferReceive: CliktCommand(help = "Retrieve coins from server") {
return@forEach
}

val statechainInfo = getStatechainInfo(transferMsg.statechainId)
val statechainInfo = getStatechainInfo(appContext.clientConfig, transferMsg.statechainId)

if (statechainInfo == null) {
println("Statechain info not found")
return@forEach
}

val isTx0OutputPubkeyValid = fiiValidateTx0OutputPubkey(statechainInfo.enclavePublicKey, transferMsg, tx0Outpoint, tx0Hex, wallet.network)

Expand Down
2 changes: 0 additions & 2 deletions clients/kotlin/src/main/kotlin/Utils.kt

This file was deleted.

2 changes: 2 additions & 0 deletions clients/kotlin/src/main/kotlin/Withdraw.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class Withdraw: CliktCommand(help = "Withdraw funds from a statecoin to a BTC ad

appContext.sqliteManager.updateWallet(wallet)

completeWithdraw(appContext.clientConfig, coin.statechainId!!, coin.signedStatechainId!!)

val json = buildJsonObject {
put("txId", txId)
}
Expand Down
124 changes: 92 additions & 32 deletions clients/kotlin/src/main/kotlin/mercurylib.kt
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,12 @@ internal interface UniffiLib : Library {
uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue

fun uniffi_mercurylib_fn_func_is_enclave_pubkey_part_of_coin(
`coin`: RustBuffer.ByValue,
`enclavePubkey`: RustBuffer.ByValue,
uniffi_out_err: UniffiRustCallStatus,
): Byte

fun uniffi_mercurylib_fn_func_new_backup_transaction(
`encodedUnsignedTx`: RustBuffer.ByValue,
`signatureHex`: RustBuffer.ByValue,
Expand Down Expand Up @@ -1174,6 +1180,8 @@ internal interface UniffiLib : Library {

fun uniffi_mercurylib_checksum_func_handle_deposit_msg_1_response(): Short

fun uniffi_mercurylib_checksum_func_is_enclave_pubkey_part_of_coin(): Short

fun uniffi_mercurylib_checksum_func_new_backup_transaction(): Short

fun uniffi_mercurylib_checksum_func_sign_message(): Short
Expand Down Expand Up @@ -1265,6 +1273,9 @@ private fun uniffiCheckApiChecksums(lib: UniffiLib) {
if (lib.uniffi_mercurylib_checksum_func_handle_deposit_msg_1_response() != 64110.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_mercurylib_checksum_func_is_enclave_pubkey_part_of_coin() != 37041.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_mercurylib_checksum_func_new_backup_transaction() != 56642.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
Expand Down Expand Up @@ -2590,7 +2601,7 @@ data class StatechainInfoResponsePayload(
@SerialName("statechain_info")
var `statechainInfo`: List<StatechainInfo>,
@SerialName("x1_pub")
var `x1Pub`: kotlin.String,
var `x1Pub`: kotlin.String?,
) {
companion object
}
Expand All @@ -2601,7 +2612,7 @@ public object FfiConverterTypeStatechainInfoResponsePayload : FfiConverterRustBu
FfiConverterString.read(buf),
FfiConverterUInt.read(buf),
FfiConverterSequenceTypeStatechainInfo.read(buf),
FfiConverterString.read(buf),
FfiConverterOptionalString.read(buf),
)
}

Expand All @@ -2610,7 +2621,7 @@ public object FfiConverterTypeStatechainInfoResponsePayload : FfiConverterRustBu
FfiConverterString.allocationSize(value.`enclavePublicKey`) +
FfiConverterUInt.allocationSize(value.`numSigs`) +
FfiConverterSequenceTypeStatechainInfo.allocationSize(value.`statechainInfo`) +
FfiConverterString.allocationSize(value.`x1Pub`)
FfiConverterOptionalString.allocationSize(value.`x1Pub`)
)

override fun write(
Expand All @@ -2620,7 +2631,7 @@ public object FfiConverterTypeStatechainInfoResponsePayload : FfiConverterRustBu
FfiConverterString.write(value.`enclavePublicKey`, buf)
FfiConverterUInt.write(value.`numSigs`, buf)
FfiConverterSequenceTypeStatechainInfo.write(value.`statechainInfo`, buf)
FfiConverterString.write(value.`x1Pub`, buf)
FfiConverterOptionalString.write(value.`x1Pub`, buf)
}
}

Expand Down Expand Up @@ -2714,34 +2725,6 @@ public object FfiConverterTypeTransferReceiverErrorResponsePayload : FfiConverte
}
}

@Serializable
data class TransferReceiverGetResponsePayload(
@SerialName("transfer_complete")
var `transferComplete`: kotlin.Boolean,
) {
companion object
}

public object FfiConverterTypeTransferReceiverGetResponsePayload : FfiConverterRustBuffer<TransferReceiverGetResponsePayload> {
override fun read(buf: ByteBuffer): TransferReceiverGetResponsePayload {
return TransferReceiverGetResponsePayload(
FfiConverterBoolean.read(buf),
)
}

override fun allocationSize(value: TransferReceiverGetResponsePayload) =
(
FfiConverterBoolean.allocationSize(value.`transferComplete`)
)

override fun write(
value: TransferReceiverGetResponsePayload,
buf: ByteBuffer,
) {
FfiConverterBoolean.write(value.`transferComplete`, buf)
}
}

@Serializable
data class TransferReceiverPostResponsePayload(
@SerialName("server_pubkey")
Expand Down Expand Up @@ -3068,6 +3051,39 @@ public object FfiConverterTypeWallet : FfiConverterRustBuffer<Wallet> {
}
}

@Serializable
data class WithdrawCompletePayload(
@SerialName("statechain_id")
var `statechainId`: kotlin.String,
@SerialName("signed_statechain_id")
var `signedStatechainId`: kotlin.String,
) {
companion object
}

public object FfiConverterTypeWithdrawCompletePayload : FfiConverterRustBuffer<WithdrawCompletePayload> {
override fun read(buf: ByteBuffer): WithdrawCompletePayload {
return WithdrawCompletePayload(
FfiConverterString.read(buf),
FfiConverterString.read(buf),
)
}

override fun allocationSize(value: WithdrawCompletePayload) =
(
FfiConverterString.allocationSize(value.`statechainId`) +
FfiConverterString.allocationSize(value.`signedStatechainId`)
)

override fun write(
value: WithdrawCompletePayload,
buf: ByteBuffer,
) {
FfiConverterString.write(value.`statechainId`, buf)
FfiConverterString.write(value.`signedStatechainId`, buf)
}
}

enum class CoinStatus {
INITIALISED,
IN_MEMPOOL,
Expand Down Expand Up @@ -3301,6 +3317,16 @@ sealed class MercuryException : Exception() {
get() = ""
}

class NoX1Pub() : MercuryException() {
override val message
get() = ""
}

class NoAggregatedPubkeyException() : MercuryException() {
override val message
get() = ""
}

companion object ErrorHandler : UniffiRustCallStatusErrorHandler<MercuryException> {
override fun lift(error_buf: RustBuffer.ByValue): MercuryException = FfiConverterTypeMercuryError.lift(error_buf)
}
Expand Down Expand Up @@ -3349,6 +3375,8 @@ public object FfiConverterTypeMercuryError : FfiConverterRustBuffer<MercuryExcep
38 -> MercuryException.InvalidT1()
39 -> MercuryException.IncorrectAggregatedPublicKey()
40 -> MercuryException.T1MustBeExactly32BytesException()
41 -> MercuryException.NoX1Pub()
42 -> MercuryException.NoAggregatedPubkeyException()
else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
}
}
Expand Down Expand Up @@ -3515,6 +3543,14 @@ public object FfiConverterTypeMercuryError : FfiConverterRustBuffer<MercuryExcep
// Add the size for the Int that specifies the variant plus the size needed for all fields
4UL
)
is MercuryException.NoX1Pub -> (
// Add the size for the Int that specifies the variant plus the size needed for all fields
4UL
)
is MercuryException.NoAggregatedPubkeyException -> (
// Add the size for the Int that specifies the variant plus the size needed for all fields
4UL
)
}
}

Expand Down Expand Up @@ -3683,6 +3719,14 @@ public object FfiConverterTypeMercuryError : FfiConverterRustBuffer<MercuryExcep
buf.putInt(40)
Unit
}
is MercuryException.NoX1Pub -> {
buf.putInt(41)
Unit
}
is MercuryException.NoAggregatedPubkeyException -> {
buf.putInt(42)
Unit
}
}.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
}
}
Expand Down Expand Up @@ -4334,6 +4378,22 @@ fun `handleDepositMsg1Response`(
)
}

@Throws(MercuryException::class)
fun `isEnclavePubkeyPartOfCoin`(
`coin`: Coin,
`enclavePubkey`: kotlin.String,
): kotlin.Boolean {
return FfiConverterBoolean.lift(
uniffiRustCallWithError(MercuryException) { _status ->
UniffiLib.INSTANCE.uniffi_mercurylib_fn_func_is_enclave_pubkey_part_of_coin(
FfiConverterTypeCoin.lower(`coin`),
FfiConverterString.lower(`enclavePubkey`),
_status,
)
},
)
}

@Throws(MercuryException::class)
fun `newBackupTransaction`(
`encodedUnsignedTx`: kotlin.String,
Expand Down
2 changes: 2 additions & 0 deletions clients/nodejs/broadcast_backup_tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const execute = async (electrumClient, db, walletName, statechainId, toAddress,

await sqlite_manager.updateWallet(db, wallet);

utils.completeWithdraw(coin.statechain_id, coin.signed_statechain_id);

return {
backupTx: backupTxTxid,
cpfpTx: cpfpTxTxid
Expand Down
Loading

0 comments on commit e5249e0

Please sign in to comment.