From 1c29cb5fff526f4fcb44fd33b100352817a782b7 Mon Sep 17 00:00:00 2001 From: Doston Kamalov Date: Wed, 6 Sep 2023 11:49:13 +0500 Subject: [PATCH] AND-4457 Added Chia unspents sort and limit to inputs --- .../blockchains/chia/ChiaAddressService.kt | 2 +- .../chia/ChiaTransactionBuilder.kt | 29 ++++++++++++++++--- .../tangem/blockchain/common/Exceptions.kt | 18 ++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/chia/ChiaAddressService.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/chia/ChiaAddressService.kt index 5bd19a452..9cedb5636 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/chia/ChiaAddressService.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/chia/ChiaAddressService.kt @@ -47,4 +47,4 @@ class ChiaAddressService(blockchain: Blockchain) : AddressService() { return Crypto.convertBits(dataBytes, 0, dataBytes.size, 5, 8, false) } } -} \ No newline at end of file +} diff --git a/blockchain/src/main/java/com/tangem/blockchain/blockchains/chia/ChiaTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/blockchains/chia/ChiaTransactionBuilder.kt index 657a9b730..7d4aca3fc 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/blockchains/chia/ChiaTransactionBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/blockchains/chia/ChiaTransactionBuilder.kt @@ -16,7 +16,9 @@ import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.extensions.Result import com.tangem.blstlib.generated.P2 import com.tangem.blstlib.generated.P2_Affine -import com.tangem.common.extensions.* +import com.tangem.common.extensions.calculateSha256 +import com.tangem.common.extensions.hexToBytes +import com.tangem.common.extensions.toHexString import java.math.BigDecimal class ChiaTransactionBuilder(private val walletPublicKey: ByteArray, val blockchain: Blockchain) { @@ -35,9 +37,20 @@ class ChiaTransactionBuilder(private val walletPublicKey: ByteArray, val blockch BlockchainSdkError.CustomError("Unspent coins are missing") ) - val change = calculateChange(transactionData, unspentCoins) + val unspentsToSpend = getUnspentsToSpend() - coinSpends = transactionData.toChiaCoinSpends(unspentCoins, change) + val change = calculateChange(transactionData, unspentsToSpend) + if (change < 0) { // unspentsToSpend not enough to cover transaction amount + val maxAmount = transactionData.amount.value!! + change.toBigDecimal().movePointLeft(blockchain.decimals()) + return Result.Failure( + BlockchainSdkError.Chia.UtxoAmountError( + maxOutputs = MAX_INPUT_COUNT, + maxAmount = maxAmount + ) + ) + } + + coinSpends = transactionData.toChiaCoinSpends(unspentsToSpend, change) val hashesForSign = coinSpends.map { it -> // our solutions are always Cons @@ -61,6 +74,12 @@ class ChiaTransactionBuilder(private val walletPublicKey: ByteArray, val blockch return (coinSpends.size * COIN_SPEND_COST) + (numberOfCoinsCreated * CREATE_COIN_COST) } + private fun getUnspentsToSpend() = unspentCoins + .sortedByDescending { it.amount } + .take(getUnspentsToSpendCount()) + + private fun getUnspentsToSpendCount(): Int = unspentCoins.size.coerceAtMost(MAX_INPUT_COUNT) + private fun calculateChange( transactionData: TransactionData, unspentCoins: List, @@ -143,6 +162,8 @@ class ChiaTransactionBuilder(private val walletPublicKey: ByteArray, val blockch private const val COIN_SPEND_COST = 4500000L private const val CREATE_COIN_COST = 2400000L + private const val MAX_INPUT_COUNT = 50 // Aligned inside Tangem. In iOS max input count is 15. + private const val AUG_SCHEME_DST = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_" } -} \ No newline at end of file +} diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/Exceptions.kt b/blockchain/src/main/java/com/tangem/blockchain/common/Exceptions.kt index 2c740dcd1..57e158e05 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/Exceptions.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/Exceptions.kt @@ -119,6 +119,23 @@ sealed class BlockchainSdkError( cause = throwable, ) + sealed class Chia( + subCode: Int, + customMessage: String? = null, + throwable: Throwable? = null, + ) : BlockchainSdkError( + code = ERROR_CODE_CHIA + subCode, + customMessage = customMessage ?: (ERROR_CODE_CHIA + subCode).toString(), + messageResId = null, + cause = throwable, + ) { + class UtxoAmountError(val maxOutputs: Int, val maxAmount: BigDecimal) : Chia( + 1, + "Due to Chia limitations only $maxOutputs UTXOs can fit in a single transaction. This means you can only" + + " send ${maxAmount.toPlainString()}." + ) + } + companion object { const val ERROR_CODE_SOLANA = 1000 const val ERROR_CODE_POLKADOT = 2000 @@ -126,6 +143,7 @@ sealed class BlockchainSdkError( const val ERROR_CODE_TON = 4000 const val ERROR_CODE_COSMOS = 5000 const val ERROR_CODE_WALLET_CORE = 6000 + const val ERROR_CODE_CHIA = 7000 } }