diff --git a/Sources/BitcoinCore/Classes/ApiSync/BlockHashFetcher.swift b/Sources/BitcoinCore/Classes/ApiSync/BlockHashFetcher.swift index 29e323cf..32b134ff 100644 --- a/Sources/BitcoinCore/Classes/ApiSync/BlockHashFetcher.swift +++ b/Sources/BitcoinCore/Classes/ApiSync/BlockHashFetcher.swift @@ -23,7 +23,7 @@ public class BlockHashFetcher: IBlockHashFetcher { } if afterCheckpoint.count > 0 { - await try blockHashes.merge(blockchairFetcher.fetch(heights: afterCheckpoint), uniquingKeysWith: { a, _ in a }) + try await blockHashes.merge(blockchairFetcher.fetch(heights: afterCheckpoint), uniquingKeysWith: { a, _ in a }) } return blockHashes diff --git a/Sources/BitcoinCore/Classes/Core/BitcoinCoreBuilder.swift b/Sources/BitcoinCore/Classes/Core/BitcoinCoreBuilder.swift index dbc34482..dceca201 100644 --- a/Sources/BitcoinCore/Classes/Core/BitcoinCoreBuilder.swift +++ b/Sources/BitcoinCore/Classes/Core/BitcoinCoreBuilder.swift @@ -32,6 +32,7 @@ public class BitcoinCoreBuilder { private var storage: IStorage? private var checkpoint: Checkpoint? private var apiSyncStateManager: ApiSyncStateManager? + private var signer: ITransactionSigner? @discardableResult public func set(extendedKey: HDExtendedKey?) -> BitcoinCoreBuilder { self.extendedKey = extendedKey @@ -42,6 +43,11 @@ public class BitcoinCoreBuilder { self.watchAddressPublicKey = watchAddressPublicKey return self } + + public func set(signer: ITransactionSigner?) -> BitcoinCoreBuilder { + self.signer = signer + return self + } public func set(network: INetwork) -> BitcoinCoreBuilder { self.network = network @@ -297,16 +303,20 @@ public class BitcoinCoreBuilder { var transactionCreator: TransactionCreator? if let hdWallet { - let ecdsaInputSigner = EcdsaInputSigner(hdWallet: hdWallet, network: network) - let schnorrInputSigner = SchnorrInputSigner(hdWallet: hdWallet) let transactionSizeCalculatorInstance = TransactionSizeCalculator() + if (self.signer == nil) { + let ecdsaInputSigner = EcdsaInputSigner(hdWallet: hdWallet, network: network) + let schnorrInputSigner = SchnorrInputSigner(hdWallet: hdWallet) + let transactionSigner = TransactionSigner(ecdsaInputSigner: ecdsaInputSigner, schnorrInputSigner: schnorrInputSigner) + self.signer = transactionSigner + } + let dustCalculatorInstance = DustCalculator(dustRelayTxFee: network.dustRelayTxFee, sizeCalculator: transactionSizeCalculatorInstance) let recipientSetter = RecipientSetter(addressConverter: addressConverter, pluginManager: pluginManager) let outputSetter = OutputSetter(outputSorterFactory: transactionDataSorterFactory, factory: factory) let inputSetter = InputSetter(unspentOutputSelector: unspentOutputSelector, transactionSizeCalculator: transactionSizeCalculatorInstance, addressConverter: addressConverter, publicKeyManager: publicKeyManager, factory: factory, pluginManager: pluginManager, dustCalculator: dustCalculatorInstance, changeScriptType: purpose.scriptType, inputSorterFactory: transactionDataSorterFactory) let lockTimeSetter = LockTimeSetter(storage: storage) - let transactionSigner = TransactionSigner(ecdsaInputSigner: ecdsaInputSigner, schnorrInputSigner: schnorrInputSigner) - let transactionBuilder = TransactionBuilder(recipientSetter: recipientSetter, inputSetter: inputSetter, lockTimeSetter: lockTimeSetter, outputSetter: outputSetter, signer: transactionSigner) + let transactionBuilder = TransactionBuilder(recipientSetter: recipientSetter, inputSetter: inputSetter, lockTimeSetter: lockTimeSetter, outputSetter: outputSetter, signer: self.signer!) transactionFeeCalculator = TransactionFeeCalculator(recipientSetter: recipientSetter, inputSetter: inputSetter, addressConverter: addressConverter, publicKeyManager: publicKeyManager, changeScriptType: purpose.scriptType) let transactionSendTimer = TransactionSendTimer(interval: 60) let transactionSenderInstance = TransactionSender(transactionSyncer: pendingTransactionSyncer, initialBlockDownload: initialDownload, peerManager: peerManager, storage: storage, timer: transactionSendTimer, logger: logger) @@ -317,7 +327,7 @@ public class BitcoinCoreBuilder { transactionSendTimer.delegate = transactionSender - transactionCreator = TransactionCreator(transactionBuilder: transactionBuilder, transactionProcessor: pendingTransactionProcessor, transactionSender: transactionSenderInstance, bloomFilterManager: bloomFilterManager) + transactionCreator = TransactionCreator(transactionBuilder: transactionBuilder, transactionProcessor: pendingTransactionProcessor, transactionSender: transactionSenderInstance, bloomFilterManager: bloomFilterManager, signer: self.signer!) } let mempoolTransactions = MempoolTransactions(transactionSyncer: pendingTransactionSyncer, transactionSender: transactionSender) diff --git a/Sources/BitcoinCore/Classes/Core/Protocols.swift b/Sources/BitcoinCore/Classes/Core/Protocols.swift index ccf8c724..9e39d6aa 100644 --- a/Sources/BitcoinCore/Classes/Core/Protocols.swift +++ b/Sources/BitcoinCore/Classes/Core/Protocols.swift @@ -378,8 +378,8 @@ public protocol ITransactionCreator { } protocol ITransactionBuilder { - func buildTransaction(toAddress: String, memo: String?, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> FullTransaction - func buildTransaction(from: UnspentOutput, toAddress: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction + func buildTransaction(toAddress: String, memo: String?, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData], signer: ITransactionSigner) throws -> FullTransaction + func buildTransaction(from: UnspentOutput, toAddress: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType, signer: ITransactionSigner) throws -> FullTransaction } protocol ITransactionFeeCalculator { @@ -401,7 +401,7 @@ public protocol IBlockchainDataListener: AnyObject { func onInsert(block: Block) } -protocol IInputSigner { +public protocol IInputSigner { func sigScriptData(transaction: Transaction, inputsToSign: [InputToSign], outputs: [Output], index: Int) throws -> [Data] } @@ -647,7 +647,7 @@ protocol ILockTimeSetter { func setLockTime(to mutableTransaction: MutableTransaction) } -protocol ITransactionSigner { +public protocol ITransactionSigner { func sign(mutableTransaction: MutableTransaction) throws } diff --git a/Sources/BitcoinCore/Classes/Transactions/Builder/TransactionBuilder.swift b/Sources/BitcoinCore/Classes/Transactions/Builder/TransactionBuilder.swift index e1ca9452..8d4e57a4 100644 --- a/Sources/BitcoinCore/Classes/Transactions/Builder/TransactionBuilder.swift +++ b/Sources/BitcoinCore/Classes/Transactions/Builder/TransactionBuilder.swift @@ -3,19 +3,17 @@ class TransactionBuilder { private let inputSetter: IInputSetter private let lockTimeSetter: ILockTimeSetter private let outputSetter: IOutputSetter - private let signer: TransactionSigner - init(recipientSetter: IRecipientSetter, inputSetter: IInputSetter, lockTimeSetter: ILockTimeSetter, outputSetter: IOutputSetter, signer: TransactionSigner) { + init(recipientSetter: IRecipientSetter, inputSetter: IInputSetter, lockTimeSetter: ILockTimeSetter, outputSetter: IOutputSetter, signer: ITransactionSigner) { self.recipientSetter = recipientSetter self.inputSetter = inputSetter self.lockTimeSetter = lockTimeSetter self.outputSetter = outputSetter - self.signer = signer } } extension TransactionBuilder: ITransactionBuilder { - func buildTransaction(toAddress: String, memo: String?, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> FullTransaction { + func buildTransaction(toAddress: String, memo: String?, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData], signer: ITransactionSigner) throws -> FullTransaction { let mutableTransaction = MutableTransaction() try recipientSetter.setRecipient(to: mutableTransaction, toAddress: toAddress, memo: memo, value: value, pluginData: pluginData, skipChecks: false) @@ -28,7 +26,7 @@ extension TransactionBuilder: ITransactionBuilder { return mutableTransaction.build() } - func buildTransaction(from unspentOutput: UnspentOutput, toAddress: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction { + func buildTransaction(from unspentOutput: UnspentOutput, toAddress: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType, signer: ITransactionSigner) throws -> FullTransaction { let mutableTransaction = MutableTransaction(outgoing: false) try recipientSetter.setRecipient(to: mutableTransaction, toAddress: toAddress, memo: memo, value: unspentOutput.output.value, pluginData: [:], skipChecks: false) diff --git a/Sources/BitcoinCore/Classes/Transactions/Builder/TransactionSigner.swift b/Sources/BitcoinCore/Classes/Transactions/Builder/TransactionSigner.swift index 518459ed..52cd7f54 100644 --- a/Sources/BitcoinCore/Classes/Transactions/Builder/TransactionSigner.swift +++ b/Sources/BitcoinCore/Classes/Transactions/Builder/TransactionSigner.swift @@ -1,6 +1,6 @@ import Foundation -class TransactionSigner { +public class TransactionSigner { enum SignError: Error { case notSupportedScriptType case noRedeemScript @@ -79,7 +79,7 @@ class TransactionSigner { } extension TransactionSigner: ITransactionSigner { - func sign(mutableTransaction: MutableTransaction) throws { + public func sign(mutableTransaction: MutableTransaction) throws { for (index, inputToSign) in mutableTransaction.inputsToSign.enumerated() { if inputToSign.previousOutput.scriptType == .p2tr { try schnorrSign(index: index, mutableTransaction: mutableTransaction) diff --git a/Sources/BitcoinCore/Classes/Transactions/TransactionCreator.swift b/Sources/BitcoinCore/Classes/Transactions/TransactionCreator.swift index 0748710a..ffdbd608 100644 --- a/Sources/BitcoinCore/Classes/Transactions/TransactionCreator.swift +++ b/Sources/BitcoinCore/Classes/Transactions/TransactionCreator.swift @@ -9,12 +9,14 @@ class TransactionCreator { private let transactionProcessor: IPendingTransactionProcessor private let transactionSender: ITransactionSender private let bloomFilterManager: IBloomFilterManager + private let signer: ITransactionSigner - init(transactionBuilder: ITransactionBuilder, transactionProcessor: IPendingTransactionProcessor, transactionSender: ITransactionSender, bloomFilterManager: IBloomFilterManager) { + init(transactionBuilder: ITransactionBuilder, transactionProcessor: IPendingTransactionProcessor, transactionSender: ITransactionSender, bloomFilterManager: IBloomFilterManager, signer: ITransactionSigner) { self.transactionBuilder = transactionBuilder self.transactionProcessor = transactionProcessor self.transactionSender = transactionSender self.bloomFilterManager = bloomFilterManager + self.signer = signer } private func processAndSend(transaction: FullTransaction) throws { @@ -40,7 +42,8 @@ extension TransactionCreator: ITransactionCreator { senderPay: senderPay, sortType: sortType, unspentOutputs: unspentOutputs, - pluginData: pluginData + pluginData: pluginData, + signer: signer ) try processAndSend(transaction: transaction) @@ -53,7 +56,8 @@ extension TransactionCreator: ITransactionCreator { toAddress: address, memo: memo, feeRate: feeRate, - sortType: sortType + sortType: sortType, + signer: self.signer ) try processAndSend(transaction: transaction) @@ -69,7 +73,8 @@ extension TransactionCreator: ITransactionCreator { senderPay: senderPay, sortType: sortType, unspentOutputs: unspentOutputs, - pluginData: pluginData + pluginData: pluginData, + signer: self.signer ) return TransactionSerializer.serialize(transaction: transaction) diff --git a/Tests/BitcoinCoreTests/Transactions/TransactionSizeCalculatorTests.swift b/Tests/BitcoinCoreTests/Transactions/TransactionSizeCalculatorTests.swift index 8e5b1137..a84a4b00 100644 --- a/Tests/BitcoinCoreTests/Transactions/TransactionSizeCalculatorTests.swift +++ b/Tests/BitcoinCoreTests/Transactions/TransactionSizeCalculatorTests.swift @@ -28,7 +28,7 @@ XCTAssertEqual(calculator.transactionSize(previousOutputs: outputs(withScriptTypes: [.p2wpkhSh]), outputScriptTypes: [.p2pkh]), 136) // 1-in 1-out (sh) witness tx XCTAssertEqual(calculator.transactionSize(previousOutputs: outputs(withScriptTypes: [.p2wpkh, .p2pkh, .p2pkh, .p2pkh]), outputScriptTypes: [.p2pkh]), 558) // 4-in 1-out witness tx } - + func testTransactionSizeShInputsStandard() { let redeemScript = Data(repeating: 0, count: 45) let shOutput = Output(withValue: 0, index: 0, lockingScript: Data(), type: .p2sh, redeemScript: redeemScript)