Skip to content

Commit

Permalink
Use specific Exceptions in ReplacementTransactionBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
omurovch committed Mar 8, 2024
1 parent 26adec5 commit ac5a6c1
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,10 @@ class ReplacementTransactionBuilder(
pluginManager.processInputs(mutableTransaction)

originalInputs.map { inputWithPreviousOutput ->
val previousOutput = inputWithPreviousOutput.previousOutput ?: throw BuildError.InvalidTransaction
val publicKey = previousOutput.publicKeyPath?.let { publicKeyManager.getPublicKeyByPath(it) } ?: throw BuildError.InvalidTransaction
val previousOutput =
inputWithPreviousOutput.previousOutput ?: throw BuildError.InvalidTransaction("No previous output of original transaction")
val publicKey = previousOutput.publicKeyPath?.let { publicKeyManager.getPublicKeyByPath(it) }
?: throw BuildError.InvalidTransaction("No public key of original transaction")
mutableTransaction.addInput(inputToSign(previousOutput = previousOutput, publicKey, incrementedSequence(inputWithPreviousOutput)))
}
}
Expand Down Expand Up @@ -256,28 +258,29 @@ class ReplacementTransactionBuilder(
minFee: Long,
type: ReplacementType
): Triple<MutableTransaction, FullTransactionInfo, List<String>> {
val originalFullInfo = storage.getFullTransactionInfo(transactionHash.toReversedByteArray()) ?: throw BuildError.InvalidTransaction
check(originalFullInfo.block == null) { "Transaction already in block" }
val originalFullInfo = storage.getFullTransactionInfo(transactionHash.toReversedByteArray())
?: throw BuildError.InvalidTransaction("No FullTransactionInfo")
check(originalFullInfo.block == null) { throw BuildError.InvalidTransaction("Transaction already in block") }

val originalFee = originalFullInfo.metadata.fee
checkNotNull(originalFee) { "No fee for original transaction" }
checkNotNull(originalFee) { throw BuildError.InvalidTransaction("No fee for original transaction") }

check(originalFullInfo.metadata.type != TransactionType.Incoming) { "Can replace only outgoing transaction" }
check(originalFullInfo.metadata.type != TransactionType.Incoming) { throw BuildError.InvalidTransaction("Can replace only outgoing transaction") }

val fixedUtxo = originalFullInfo.inputs.mapNotNull { it.previousOutput }

check(originalFullInfo.inputs.size == fixedUtxo.size) { "No previous output" }
check(originalFullInfo.inputs.size == fixedUtxo.size) { throw BuildError.NoPreviousOutput }

check(originalFullInfo.inputs.any { it.input.rbfEnabled }) { "Rbf not enabled" }
check(originalFullInfo.inputs.any { it.input.rbfEnabled }) { throw BuildError.RbfNotEnabled }

val originalSize = sizeCalculator.transactionSize(previousOutputs = fixedUtxo, outputs = originalFullInfo.outputs)

val originalFeeRate = (originalFee / originalSize).toInt()
val descendantTransactions = storage.getDescendantTransactionsFullInfo(transactionHash.toReversedByteArray())
val absoluteFee = descendantTransactions.sumOf { it.metadata.fee ?: 0 }

check(descendantTransactions.all { it.header.conflictingTxHash == null }) { "Already replaced" }
check(absoluteFee <= minFee) { "Fee too low" }
check(descendantTransactions.all { it.header.conflictingTxHash == null }) { throw BuildError.InvalidTransaction("Already replaced") }
check(absoluteFee <= minFee) { throw BuildError.FeeTooLow }

val mutableTransaction = when (type) {
ReplacementType.SpeedUp -> speedUpReplacement(originalFullInfo, minFee, originalFeeRate, fixedUtxo)
Expand All @@ -291,7 +294,7 @@ class ReplacementTransactionBuilder(
)
}

checkNotNull(mutableTransaction) { "Unable to replace" }
checkNotNull(mutableTransaction) { throw BuildError.UnableToReplace }

val fullTransaction = mutableTransaction.build()
metadataExtractor.extract(fullTransaction)
Expand All @@ -314,14 +317,14 @@ class ReplacementTransactionBuilder(

fun replacementInfo(transactionHash: String, type: ReplacementType): ReplacementTransactionInfo? {
val originalFullInfo = storage.getFullTransactionInfo(transactionHash.toReversedByteArray()) ?: return null
check(originalFullInfo.block == null) { "Transaction already in block" }
check(originalFullInfo.metadata.type != TransactionType.Incoming) { "Can replace only outgoing transaction" }
check(originalFullInfo.block == null) { throw BuildError.InvalidTransaction("Transaction already in block") }
check(originalFullInfo.metadata.type != TransactionType.Incoming) { throw BuildError.InvalidTransaction("Can replace only outgoing transaction") }

val originalFee = originalFullInfo.metadata.fee
checkNotNull(originalFee) { "No fee for original transaction" }
checkNotNull(originalFee) { throw BuildError.InvalidTransaction("No fee for original transaction") }

val fixedUtxo = originalFullInfo.inputs.mapNotNull { it.previousOutput }
check(originalFullInfo.inputs.size == fixedUtxo.size) { "No previous output" }
check(originalFullInfo.inputs.size == fixedUtxo.size) { throw BuildError.NoPreviousOutput}

val descendantTransactions = storage.getDescendantTransactionsFullInfo(transactionHash.toReversedByteArray())
val absoluteFee = descendantTransactions.sumOf { it.metadata.fee ?: 0 }
Expand Down Expand Up @@ -370,7 +373,7 @@ class ReplacementTransactionBuilder(
}

sealed class BuildError : Throwable() {
object InvalidTransaction : BuildError()
class InvalidTransaction(override val message: String) : BuildError()
object NoPreviousOutput : BuildError()
object FeeTooLow : BuildError()
object RbfNotEnabled : BuildError()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,6 @@ interface TransactionInputDao {
@Query("select * from TransactionInput where transactionHash IN (:hashes)")
fun getTransactionInputs(hashes: List<ByteArray>): List<TransactionInput>

// @Query(
// """
// SELECT
// inputs.*,
// outputs.*
// FROM TransactionInput as inputs
// LEFT JOIN TransactionOutput AS outputs ON outputs.transactionHash = inputs.previousOutputTxHash AND outputs.`index` = inputs.previousOutputIndex
// WHERE inputs.transactionHash IN(:txHashes)
// """
// )
// fun getInputsWithPrevouts(txHashes: List<ByteArray>): List<InputWithPreviousOutput>

@Query("select * from TransactionOutput where transactionHash=:transactionHash AND `index`=:index limit 1")
fun output(transactionHash: ByteArray, index: Long): TransactionOutput?

Expand Down

0 comments on commit ac5a6c1

Please sign in to comment.