Skip to content

Commit 945cf2c

Browse files
committed
Add parsing memo field from op_return
1 parent e282683 commit 945cf2c

17 files changed

Lines changed: 98 additions & 60 deletions

Sources/BitcoinCore/Classes/Core/AbstractKit.swift

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,24 @@ open class AbstractKit {
4141
bitcoinCore.transaction(hash: hash)
4242
}
4343

44-
open func send(to address: String, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutputInfo]? = nil, pluginData: [UInt8: IPluginData] = [:]) throws -> FullTransaction {
45-
try bitcoinCore.send(to: address, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: unspentOutputs, pluginData: pluginData)
44+
open func send(to address: String, memo: String?, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutputInfo]? = nil, pluginData: [UInt8: IPluginData] = [:]) throws -> FullTransaction {
45+
try bitcoinCore.send(to: address, memo: memo, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: unspentOutputs, pluginData: pluginData)
4646
}
4747

48-
public func send(to hash: Data, scriptType: ScriptType, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutputInfo]?) throws -> FullTransaction {
49-
try bitcoinCore.send(to: hash, scriptType: scriptType, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: unspentOutputs)
48+
public func send(to hash: Data, memo: String?, scriptType: ScriptType, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutputInfo]?) throws -> FullTransaction {
49+
try bitcoinCore.send(to: hash, memo: memo, scriptType: scriptType, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: unspentOutputs)
5050
}
5151

52-
public func send(to hash: Data, scriptType: ScriptType, value: Int, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction {
53-
try bitcoinCore.send(to: hash, scriptType: scriptType, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: nil)
52+
public func send(to hash: Data, memo: String?, scriptType: ScriptType, value: Int, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction {
53+
try bitcoinCore.send(to: hash, memo: memo, scriptType: scriptType, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: nil)
5454
}
5555

56-
public func redeem(from unspentOutput: UnspentOutput, to address: String, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction {
57-
try bitcoinCore.redeem(from: unspentOutput, to: address, feeRate: feeRate, sortType: sortType)
56+
public func redeem(from unspentOutput: UnspentOutput, to address: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction {
57+
try bitcoinCore.redeem(from: unspentOutput, memo: memo, to: address, feeRate: feeRate, sortType: sortType)
5858
}
5959

60-
open func createRawTransaction(to address: String, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]? = nil, pluginData: [UInt8: IPluginData] = [:]) throws -> Data {
61-
try bitcoinCore.createRawTransaction(to: address, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: unspentOutputs, pluginData: pluginData)
60+
open func createRawTransaction(to address: String, memo: String?, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]? = nil, pluginData: [UInt8: IPluginData] = [:]) throws -> Data {
61+
try bitcoinCore.createRawTransaction(to: address, memo: memo, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: unspentOutputs, pluginData: pluginData)
6262
}
6363

6464
open func validate(address: String, pluginData: [UInt8: IPluginData] = [:]) throws {
@@ -69,13 +69,13 @@ open class AbstractKit {
6969
bitcoinCore.parse(paymentAddress: paymentAddress)
7070
}
7171

72-
open func sendInfo(for value: Int, toAddress: String? = nil, feeRate: Int, unspentOutputs: [UnspentOutputInfo]?, pluginData: [UInt8: IPluginData] = [:]) throws -> BitcoinSendInfo {
72+
open func sendInfo(for value: Int, toAddress: String? = nil, memo: String?, feeRate: Int, unspentOutputs: [UnspentOutputInfo]?, pluginData: [UInt8: IPluginData] = [:]) throws -> BitcoinSendInfo {
7373
let outputs = unspentOutputs.map { $0.outputs(from: bitcoinCore.unspentOutputs) }
74-
return try bitcoinCore.sendInfo(for: value, toAddress: toAddress, feeRate: feeRate, unspentOutputs: outputs, pluginData: pluginData)
74+
return try bitcoinCore.sendInfo(for: value, toAddress: toAddress, memo: memo, feeRate: feeRate, unspentOutputs: outputs, pluginData: pluginData)
7575
}
7676

77-
open func maxSpendableValue(toAddress: String? = nil, feeRate: Int, unspentOutputs: [UnspentOutputInfo]?, pluginData: [UInt8: IPluginData] = [:]) throws -> Int {
78-
try bitcoinCore.maxSpendableValue(toAddress: toAddress, feeRate: feeRate, unspentOutputs: unspentOutputs, pluginData: pluginData)
77+
open func maxSpendableValue(toAddress: String? = nil, memo: String?, feeRate: Int, unspentOutputs: [UnspentOutputInfo]?, pluginData: [UInt8: IPluginData] = [:]) throws -> Int {
78+
try bitcoinCore.maxSpendableValue(toAddress: toAddress, memo: memo, feeRate: feeRate, unspentOutputs: unspentOutputs, pluginData: pluginData)
7979
}
8080

8181
open func maxSpendLimit(pluginData: [UInt8: IPluginData]) throws -> Int? {

Sources/BitcoinCore/Classes/Core/BaseTransactionInfoConverter.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ public class BaseTransactionInfoConverter: IBaseTransactionInfoConverter {
4444
outputInfo.pluginId = pluginId
4545
outputInfo.pluginDataString = pluginDataString
4646
outputInfo.pluginData = pluginManager.parsePluginData(fromPlugin: pluginId, pluginDataString: pluginDataString, transactionTimestamp: transactionTimestamp)
47+
} else if output.scriptType == .nullData, let payload = output.lockingScriptPayload, !payload.isEmpty {
48+
// read first byte to get data length and parse first message
49+
let byteStream = ByteStream(payload)
50+
let _ = byteStream.read(UInt8.self) // read op_return
51+
let length = byteStream.read(VarInt.self).underlyingValue
52+
if byteStream.availableBytes >= length {
53+
let data = byteStream.read(Data.self, count: Int(length))
54+
outputInfo.memo = String(data: data, encoding: .utf8) //todo: make memo manager if need parse not only memo (some instructions)
55+
}
4756
}
4857

4958
outputsInfo.append(outputInfo)

Sources/BitcoinCore/Classes/Core/BitcoinCore.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,42 +166,43 @@ public extension BitcoinCore {
166166
}
167167
}
168168

169-
func send(to address: String, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutputInfo]?, pluginData: [UInt8: IPluginData] = [:]) throws -> FullTransaction {
169+
func send(to address: String, memo: String?, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutputInfo]?, pluginData: [UInt8: IPluginData] = [:]) throws -> FullTransaction {
170170
guard let transactionCreator else {
171171
throw CoreError.readOnlyCore
172172
}
173+
173174
let outputs = unspentOutputs.map { $0.outputs(from: unspentOutputSelector.all) }
174-
return try transactionCreator.create(to: address, value: value, feeRate: feeRate, senderPay: true, sortType: sortType, unspentOutputs: outputs, pluginData: pluginData)
175+
return try transactionCreator.create(to: address, memo: memo, value: value, feeRate: feeRate, senderPay: true, sortType: sortType, unspentOutputs: outputs, pluginData: pluginData)
175176
}
176177

177-
func send(to address: String, value: Int, feeRate: Int, sortType: TransactionDataSortType, pluginData: [UInt8: IPluginData]) throws -> FullTransaction {
178-
try send(to: address, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: nil, pluginData: pluginData)
178+
func send(to address: String, memo: String?, value: Int, feeRate: Int, sortType: TransactionDataSortType, pluginData: [UInt8: IPluginData]) throws -> FullTransaction {
179+
try send(to: address, memo: memo, value: value, feeRate: feeRate, sortType: sortType, unspentOutputs: nil, pluginData: pluginData)
179180
}
180181

181-
func send(to hash: Data, scriptType: ScriptType, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutputInfo]?) throws -> FullTransaction {
182+
func send(to hash: Data, memo: String?, scriptType: ScriptType, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutputInfo]?) throws -> FullTransaction {
182183
guard let transactionCreator else {
183184
throw CoreError.readOnlyCore
184185
}
185186

186187
let outputs = unspentOutputs.map { $0.outputs(from: unspentOutputSelector.all) }
187188
let toAddress = try addressConverter.convert(lockingScriptPayload: hash, type: scriptType)
188-
return try transactionCreator.create(to: toAddress.stringValue, value: value, feeRate: feeRate, senderPay: true, sortType: sortType, unspentOutputs: outputs, pluginData: [:])
189+
return try transactionCreator.create(to: toAddress.stringValue, memo: memo, value: value, feeRate: feeRate, senderPay: true, sortType: sortType, unspentOutputs: outputs, pluginData: [:])
189190
}
190191

191-
internal func redeem(from unspentOutput: UnspentOutput, to address: String, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction {
192+
internal func redeem(from unspentOutput: UnspentOutput, memo: String?, to address: String, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction {
192193
guard let transactionCreator else {
193194
throw CoreError.readOnlyCore
194195
}
195196

196-
return try transactionCreator.create(from: unspentOutput, to: address, feeRate: feeRate, sortType: sortType)
197+
return try transactionCreator.create(from: unspentOutput, to: address, memo: memo, feeRate: feeRate, sortType: sortType)
197198
}
198199

199-
func createRawTransaction(to address: String, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData] = [:]) throws -> Data {
200+
func createRawTransaction(to address: String, memo: String?, value: Int, feeRate: Int, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData] = [:]) throws -> Data {
200201
guard let transactionCreator else {
201202
throw CoreError.readOnlyCore
202203
}
203204

204-
return try transactionCreator.createRawTransaction(to: address, value: value, feeRate: feeRate, senderPay: true, sortType: sortType, unspentOutputs: unspentOutputs, pluginData: pluginData)
205+
return try transactionCreator.createRawTransaction(to: address, memo: memo, value: value, feeRate: feeRate, senderPay: true, sortType: sortType, unspentOutputs: unspentOutputs, pluginData: pluginData)
205206
}
206207

207208
func validate(address: String, pluginData: [UInt8: IPluginData] = [:]) throws {
@@ -212,23 +213,23 @@ public extension BitcoinCore {
212213
paymentAddressParser.parse(paymentAddress: paymentAddress)
213214
}
214215

215-
func sendInfo(for value: Int, toAddress: String? = nil, feeRate: Int, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData] = [:]) throws -> BitcoinSendInfo {
216+
func sendInfo(for value: Int, toAddress: String? = nil, memo: String?, feeRate: Int, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData] = [:]) throws -> BitcoinSendInfo {
216217
guard let transactionFeeCalculator else {
217218
throw CoreError.readOnlyCore
218219
}
219220

220-
return try transactionFeeCalculator.sendInfo(for: value, feeRate: feeRate, senderPay: true, toAddress: toAddress, unspentOutputs: unspentOutputs, pluginData: pluginData)
221+
return try transactionFeeCalculator.sendInfo(for: value, feeRate: feeRate, senderPay: true, toAddress: toAddress, memo: memo, unspentOutputs: unspentOutputs, pluginData: pluginData)
221222
}
222223

223-
func maxSpendableValue(toAddress: String? = nil, feeRate: Int, unspentOutputs: [UnspentOutputInfo]?, pluginData: [UInt8: IPluginData] = [:]) throws -> Int {
224+
func maxSpendableValue(toAddress: String? = nil, memo: String?, feeRate: Int, unspentOutputs: [UnspentOutputInfo]?, pluginData: [UInt8: IPluginData] = [:]) throws -> Int {
224225
guard let transactionFeeCalculator else {
225226
throw CoreError.readOnlyCore
226227
}
227228

228229
let outputs = unspentOutputs.map { $0.outputs(from: unspentOutputSelector.all) }
229230
let balance = outputs.map { $0.map(\.output.value).reduce(0, +) } ?? balance.spendable
230231

231-
let sendAllFee = try transactionFeeCalculator.sendInfo(for: balance, feeRate: feeRate, senderPay: false, toAddress: toAddress, unspentOutputs: outputs, pluginData: pluginData).fee
232+
let sendAllFee = try transactionFeeCalculator.sendInfo(for: balance, feeRate: feeRate, senderPay: false, toAddress: toAddress, memo: memo, unspentOutputs: outputs, pluginData: pluginData).fee
232233
return max(0, balance - sendAllFee)
233234
}
234235

Sources/BitcoinCore/Classes/Core/Protocols.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -373,18 +373,18 @@ public protocol ITransactionSyncer: AnyObject {
373373
}
374374

375375
public protocol ITransactionCreator {
376-
func create(to address: String, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> FullTransaction
377-
func create(from: UnspentOutput, to address: String, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction
378-
func createRawTransaction(to address: String, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> Data
376+
func create(to address: String, memo: String?, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> FullTransaction
377+
func create(from: UnspentOutput, to address: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction
378+
func createRawTransaction(to address: String, memo: String?, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> Data
379379
}
380380

381381
protocol ITransactionBuilder {
382-
func buildTransaction(toAddress: String, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> FullTransaction
383-
func buildTransaction(from: UnspentOutput, toAddress: String, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction
382+
func buildTransaction(toAddress: String, memo: String?, value: Int, feeRate: Int, senderPay: Bool, sortType: TransactionDataSortType, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> FullTransaction
383+
func buildTransaction(from: UnspentOutput, toAddress: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType) throws -> FullTransaction
384384
}
385385

386386
protocol ITransactionFeeCalculator {
387-
func sendInfo(for value: Int, feeRate: Int, senderPay: Bool, toAddress: String?, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> BitcoinSendInfo
387+
func sendInfo(for value: Int, feeRate: Int, senderPay: Bool, toAddress: String?, memo: String?, unspentOutputs: [UnspentOutput]?, pluginData: [UInt8: IPluginData]) throws -> BitcoinSendInfo
388388
}
389389

390390
protocol IBlockchain {
@@ -407,8 +407,8 @@ protocol IInputSigner {
407407
}
408408

409409
public protocol ITransactionSizeCalculator {
410-
func transactionSize(previousOutputs: [Output], outputScriptTypes: [ScriptType]) -> Int
411-
func transactionSize(previousOutputs: [Output], outputScriptTypes: [ScriptType], pluginDataOutputSize: Int) -> Int
410+
func transactionSize(previousOutputs: [Output], outputScriptTypes: [ScriptType], memo: String?) -> Int
411+
func transactionSize(previousOutputs: [Output], outputScriptTypes: [ScriptType], memo: String?, pluginDataOutputSize: Int) -> Int
412412
func outputSize(type: ScriptType) -> Int
413413
func inputSize(type: ScriptType) -> Int
414414
func witnessSize(type: ScriptType) -> Int
@@ -422,7 +422,7 @@ public protocol IDustCalculator {
422422
public protocol IUnspentOutputSelector {
423423
var all: [UnspentOutput] { get }
424424

425-
func select(value: Int, feeRate: Int, outputScriptType: ScriptType, changeType: ScriptType, senderPay: Bool, pluginDataOutputSize: Int) throws -> SelectedUnspentOutputInfo
425+
func select(value: Int, memo: String?, feeRate: Int, outputScriptType: ScriptType, changeType: ScriptType, senderPay: Bool, pluginDataOutputSize: Int) throws -> SelectedUnspentOutputInfo
426426
}
427427

428428
public protocol IUnspentOutputProvider {
@@ -632,7 +632,7 @@ public protocol IBlockMedianTimeHelper {
632632
}
633633

634634
protocol IRecipientSetter {
635-
func setRecipient(to mutableTransaction: MutableTransaction, toAddress: String, value: Int, pluginData: [UInt8: IPluginData], skipChecks: Bool) throws
635+
func setRecipient(to mutableTransaction: MutableTransaction, toAddress: String, memo: String?, value: Int, pluginData: [UInt8: IPluginData], skipChecks: Bool) throws
636636
}
637637

638638
protocol IOutputSetter {

Sources/BitcoinCore/Classes/Helpers/UnspentOutputSelectorChain.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ class UnspentOutputSelectorChain: IUnspentOutputSelector {
1010
provider.spendableUtxo
1111
}
1212

13-
func select(value: Int, feeRate: Int, outputScriptType: ScriptType, changeType: ScriptType, senderPay: Bool, pluginDataOutputSize: Int) throws -> SelectedUnspentOutputInfo {
13+
func select(value: Int, memo: String?, feeRate: Int, outputScriptType: ScriptType, changeType: ScriptType, senderPay: Bool, pluginDataOutputSize: Int) throws -> SelectedUnspentOutputInfo {
1414
var lastError: Error = BitcoinCoreErrors.Unexpected.unknown
1515

1616
for selector in concreteSelectors {
1717
do {
18-
return try selector.select(value: value, feeRate: feeRate, outputScriptType: outputScriptType, changeType: changeType, senderPay: senderPay, pluginDataOutputSize: pluginDataOutputSize)
18+
return try selector.select(value: value, memo: memo, feeRate: feeRate, outputScriptType: outputScriptType, changeType: changeType, senderPay: senderPay, pluginDataOutputSize: pluginDataOutputSize)
1919
} catch {
2020
lastError = error
2121
}

Sources/BitcoinCore/Classes/Managers/UnspentOutputQueue.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class UnspentOutputQueue {
6363
let feeWithoutChange = sizeCalculator.transactionSize(
6464
previousOutputs: selectedOutputs.map(\.output),
6565
outputScriptTypes: [params.outputScriptType],
66+
memo: params.memo,
6667
pluginDataOutputSize: params.pluginDataOutputSize
6768
) * params.fee
6869

@@ -86,6 +87,7 @@ class UnspentOutputQueue {
8687
struct Parameters {
8788
let value: Int
8889
let senderPay: Bool
90+
let memo: String?
8991
let fee: Int
9092

9193
let outputsLimit: Int?

Sources/BitcoinCore/Classes/Managers/UnspentOutputSelector.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ extension UnspentOutputSelector: IUnspentOutputSelector {
3131
provider.spendableUtxo
3232
}
3333

34-
public func select(value: Int, feeRate: Int, outputScriptType: ScriptType = .p2pkh, changeType: ScriptType = .p2pkh, senderPay: Bool, pluginDataOutputSize: Int) throws -> SelectedUnspentOutputInfo {
34+
public func select(value: Int, memo: String?, feeRate: Int, outputScriptType: ScriptType = .p2pkh, changeType: ScriptType = .p2pkh, senderPay: Bool, pluginDataOutputSize: Int) throws -> SelectedUnspentOutputInfo {
3535
let sortedOutputs = provider.spendableUtxo.sorted(by: { lhs, rhs in
3636
(lhs.output.failedToSpend && !rhs.output.failedToSpend) || (
3737
lhs.output.failedToSpend == rhs.output.failedToSpend && lhs.output.value < rhs.output.value
@@ -46,6 +46,7 @@ extension UnspentOutputSelector: IUnspentOutputSelector {
4646
let params = UnspentOutputQueue.Parameters(
4747
value: value,
4848
senderPay: senderPay,
49+
memo: memo,
4950
fee: feeRate,
5051
outputsLimit: outputsLimit,
5152
outputScriptType: outputScriptType,

0 commit comments

Comments
 (0)