Skip to content
This repository has been archived by the owner on Oct 28, 2022. It is now read-only.

Commit

Permalink
Merge branch 'master' into production
Browse files Browse the repository at this point in the history
  • Loading branch information
ealymbaev committed Feb 28, 2019
2 parents 34c8079 + 22c6e2f commit 0272c54
Show file tree
Hide file tree
Showing 48 changed files with 1,026 additions and 724 deletions.
2 changes: 1 addition & 1 deletion HSBitcoinKit.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = 'HSBitcoinKit'
spec.version = '0.3'
spec.version = '0.4'
spec.summary = 'Bitcoin wallet library for Swift'
spec.description = <<-DESC
HSBitcoinKit implements Bitcoin protocol in Swift. It is an implementation of the Bitcoin SPV protocol written (almost) entirely in swift.
Expand Down
74 changes: 58 additions & 16 deletions HSBitcoinKit/HSBitcoinKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion HSBitcoinKit/HSBitcoinKit/Blocks/BlockSyncer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ extension BlockSyncer: IBlockSyncer {
}

if blockLocatorHashes.isEmpty {
realm.objects(Block.self).sorted(byKeyPath: "height", ascending: false).prefix(10).forEach { block in
realm.objects(Block.self).filter("height > %@", network.checkpointBlock.height).sorted(byKeyPath: "height", ascending: false).prefix(10).forEach { block in
blockLocatorHashes.append(block.headerHash)
}
}
Expand Down
13 changes: 12 additions & 1 deletion HSBitcoinKit/HSBitcoinKit/Blocks/Blockchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import RealmSwift
class Blockchain {
private let network: INetwork
private let factory: IFactory
weak var listener: IBlockchainDataListener?

init(network: INetwork, factory: IFactory) {
init(network: INetwork, factory: IFactory, listener: IBlockchainDataListener? = nil) {
self.network = network
self.factory = factory
self.listener = listener
}

}
Expand All @@ -28,13 +30,17 @@ extension Blockchain: IBlockchain {
block.stale = true
realm.add(block)

listener?.onInsert(block: block)

return block
}

func forceAdd(merkleBlock: MerkleBlock, height: Int, realm: Realm) -> Block {
let block = factory.block(withHeader: merkleBlock.header, height: height)
realm.add(block)

listener?.onInsert(block: block)

return block
}

Expand Down Expand Up @@ -65,15 +71,20 @@ extension Blockchain: IBlockchain {
}

func deleteBlocks(blocks: Results<Block>, realm: Realm) {
var hashes = [String]()
for block in blocks {
for transaction in block.transactions {
realm.delete(transaction.outputs)
realm.delete(transaction.inputs)
}
hashes.append(contentsOf: block.transactions.map { $0.reversedHashHex })

realm.delete(block.transactions)
}

realm.delete(blocks)

listener?.onDelete(transactionHashes: hashes)
}

}
76 changes: 64 additions & 12 deletions HSBitcoinKit/HSBitcoinKit/Core/BitcoinKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import RxSwift
public class BitcoinKit {

public weak var delegate: BitcoinKitDelegate?
public var delegateQueue = DispatchQueue(label: "bitcoin_delegate_queue")

private var unspentOutputsNotificationToken: NotificationToken?
private var transactionsNotificationToken: NotificationToken?
Expand All @@ -29,7 +30,9 @@ public class BitcoinKit {
private let reachabilityManager: ReachabilityManager
private let peerHostManager: IPeerHostManager
private let stateManager: IStateManager
private let initialSyncApi: IInitialSyncApi

private let blockDiscovery: IBlockDiscovery

private let ipfsApi: IFeeRateApi
private let addressManager: IAddressManager
private let bloomFilterManager: IBloomFilterManager
Expand Down Expand Up @@ -70,7 +73,7 @@ public class BitcoinKit {
private let blockSyncer: IBlockSyncer

private let kitStateProvider: IKitStateProvider & ISyncStateListener
private var dataProvider: IDataProvider
private var dataProvider: IDataProvider & IBlockchainDataListener

public init(withWords words: [String], coin: Coin, walletId: String, newWallet: Bool = false, confirmationsThreshold: Int = 6, minLogLevel: Logger.Level = .verbose) {
let realmFileName = "\(walletId)-\(coin.rawValue).realm"
Expand Down Expand Up @@ -124,7 +127,13 @@ public class BitcoinKit {
}

// initialSyncApi = BtcComApi(network: network, logger: logger)
initialSyncApi = InitialSyncApi(network: network, logger: logger)
let bcoinApiManager = BCoinApiManager(network: network, logger: logger)

let blockHashFetcherHelper = BlockHashFetcherHelper()
let blockHashFetcher = BlockHashFetcher(addressSelector: addressSelector, apiManager: bcoinApiManager, helper: blockHashFetcherHelper)

blockDiscovery = BlockDiscoveryBatch(network: network, wallet: hdWallet, blockHashFetcher: blockHashFetcher, logger: logger)

feeRateApiProvider = FeeRateApiProvider()
ipfsApi = IpfsApi(network: network, apiProvider: feeRateApiProvider, logger: logger)

Expand All @@ -139,7 +148,7 @@ public class BitcoinKit {
peerGroup = PeerGroup(factory: factory, network: network, listener: kitStateProvider, reachabilityManager: reachabilityManager, peerHostManager: peerHostManager, bloomFilterManager: bloomFilterManager, logger: logger)

addressManager = AddressManager(realmFactory: realmFactory, hdWallet: hdWallet, addressConverter: addressConverter)
initialSyncer = InitialSyncer(realmFactory: realmFactory, listener: kitStateProvider, hdWallet: hdWallet, stateManager: stateManager, api: initialSyncApi, addressManager: addressManager, addressSelector: addressSelector, factory: factory, peerGroup: peerGroup, network: network, logger: logger)
initialSyncer = InitialSyncer(realmFactory: realmFactory, listener: kitStateProvider, hdWallet: hdWallet, stateManager: stateManager, blockDiscovery: blockDiscovery, addressManager: addressManager, factory: factory, peerGroup: peerGroup, reachabilityManager: reachabilityManager, logger: logger)

realmStorage = RealmStorage(realmFactory: realmFactory)

Expand Down Expand Up @@ -167,15 +176,18 @@ public class BitcoinKit {
transactionSyncer = TransactionSyncer(realmFactory: realmFactory, processor: transactionProcessor, addressManager: addressManager, bloomFilterManager: bloomFilterManager)
transactionBuilder = TransactionBuilder(unspentOutputSelector: unspentOutputSelector, unspentOutputProvider: unspentOutputProvider, addressManager: addressManager, addressConverter: addressConverter, inputSigner: inputSigner, scriptBuilder: scriptBuilder, factory: factory)
transactionCreator = TransactionCreator(realmFactory: realmFactory, transactionBuilder: transactionBuilder, transactionProcessor: transactionProcessor, peerGroup: peerGroup)
blockchain = Blockchain(network: network, factory: factory)

dataProvider = DataProvider(realmFactory: realmFactory, addressManager: addressManager, addressConverter: addressConverter, paymentAddressParser: paymentAddressParser, unspentOutputProvider: unspentOutputProvider, feeRateManager: feeRateManager, transactionCreator: transactionCreator, transactionBuilder: transactionBuilder, network: network)

blockchain = Blockchain(network: network, factory: factory, listener: dataProvider)
blockSyncer = BlockSyncer(realmFactory: realmFactory, network: network, listener: kitStateProvider, transactionProcessor: transactionProcessor, blockchain: blockchain, addressManager: addressManager, bloomFilterManager: bloomFilterManager, logger: logger)

peerGroup.blockSyncer = blockSyncer
peerGroup.transactionSyncer = transactionSyncer

kitStateProvider.delegate = self
transactionProcessor.listener = dataProvider

dataProvider.delegate = self
}

Expand All @@ -189,7 +201,7 @@ extension BitcoinKit {
}

public func clear() throws {
peerGroup.stop()
initialSyncer.stop()

let realm = realmFactory.realm

Expand All @@ -210,6 +222,10 @@ extension BitcoinKit {
return dataProvider.balance
}

public var syncState: BitcoinKit.KitState {
return kitStateProvider.syncState
}

public func transactions(fromHash: String? = nil, limit: Int? = nil) -> Single<[TransactionInfo]> {
return dataProvider.transactions(fromHash: fromHash, limit: limit)
}
Expand Down Expand Up @@ -242,28 +258,49 @@ extension BitcoinKit {

extension BitcoinKit: IDataProviderDelegate {

func transactionsUpdated(inserted: [TransactionInfo], updated: [TransactionInfo], deleted: [Int]) {
delegate?.transactionsUpdated(bitcoinKit: self, inserted: inserted, updated: updated, deleted: deleted)
func transactionsUpdated(inserted: [TransactionInfo], updated: [TransactionInfo]) {
delegateQueue.async { [weak self] in
if let kit = self {
kit.delegate?.transactionsUpdated(bitcoinKit: kit, inserted: inserted, updated: updated)
}
}
}

func transactionsDeleted(hashes: [String]) {
delegateQueue.async { [weak self] in
self?.delegate?.transactionsDeleted(hashes: hashes)
}
}

func balanceUpdated(balance: Int) {
delegate?.balanceUpdated(bitcoinKit: self, balance: balance)
delegateQueue.async { [weak self] in
if let kit = self {
kit.delegate?.balanceUpdated(bitcoinKit: kit, balance: balance)
}
}
}

func lastBlockInfoUpdated(lastBlockInfo: BlockInfo) {
delegate?.lastBlockInfoUpdated(bitcoinKit: self, lastBlockInfo: lastBlockInfo)
delegateQueue.async { [weak self] in
if let kit = self {
kit.delegate?.lastBlockInfoUpdated(bitcoinKit: kit, lastBlockInfo: lastBlockInfo)
}
}
}

}

extension BitcoinKit: IKitStateProviderDelegate {
func handleKitStateUpdate(state: KitState) {
delegate?.kitStateUpdated(state: state)
delegateQueue.async { [weak self] in
self?.delegate?.kitStateUpdated(state: state)
}
}
}

public protocol BitcoinKitDelegate: class {
func transactionsUpdated(bitcoinKit: BitcoinKit, inserted: [TransactionInfo], updated: [TransactionInfo], deleted: [Int])
func transactionsUpdated(bitcoinKit: BitcoinKit, inserted: [TransactionInfo], updated: [TransactionInfo])
func transactionsDeleted(hashes: [String])
func balanceUpdated(bitcoinKit: BitcoinKit, balance: Int)
func lastBlockInfoUpdated(bitcoinKit: BitcoinKit, lastBlockInfo: BlockInfo)
func kitStateUpdated(state: BitcoinKit.KitState)
Expand Down Expand Up @@ -298,3 +335,18 @@ extension BitcoinKit {
}

}

extension BitcoinKit.KitState {

public static func == (lhs: BitcoinKit.KitState, rhs: BitcoinKit.KitState) -> Bool {
switch (lhs, rhs) {
case (.synced, .synced), (.notSynced, .notSynced):
return true
case (.syncing(progress: let leftProgress), .syncing(progress: let rightProgress)):
return leftProgress == rightProgress
default:
return false
}
}

}
87 changes: 37 additions & 50 deletions HSBitcoinKit/HSBitcoinKit/Core/DataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ class DataProvider {

private let balanceUpdateSubject = PublishSubject<Void>()

private var transactionsNotificationToken: NotificationToken?
private var blocksNotificationToken: NotificationToken?

public var balance: Int = 0
public var balance: Int = 0 {
didSet {
if !(oldValue == balance) {
delegate?.balanceUpdated(balance: balance)
}
}
}
public var lastBlockInfo: BlockInfo? = nil

weak var delegate: IDataProviderDelegate?
Expand All @@ -39,54 +42,11 @@ class DataProvider {
self.transactionBuilder = transactionBuilder
self.network = network
self.balance = unspentOutputProvider.balance
self.lastBlockInfo = blockRealmResults.last.map { blockInfo(fromBlock: $0) }
self.lastBlockInfo = realmFactory.realm.objects(Block.self).sorted(byKeyPath: "height").last.map { blockInfo(fromBlock: $0) }

balanceUpdateSubject.debounce(debounceTime, scheduler: MainScheduler.instance).subscribeAsync(disposeBag: disposeBag, onNext: {
balanceUpdateSubject.debounce(debounceTime, scheduler: ConcurrentDispatchQueueScheduler(qos: .background)).subscribe(onNext: {
self.balance = unspentOutputProvider.balance
self.delegate?.balanceUpdated(balance: self.balance)
})

transactionsNotificationToken = transactionRealmResults.observe { [weak self] changeset in
self?.handleTransactions(changeset: changeset)
}

blocksNotificationToken = blockRealmResults.observe { [weak self] changeset in
self?.handleBlocks(changeset: changeset)
}
}

deinit {
transactionsNotificationToken?.invalidate()
blocksNotificationToken?.invalidate()
}

private func handleTransactions(changeset: RealmCollectionChange<Results<Transaction>>) {
if case let .update(collection, deletions, insertions, modifications) = changeset {
delegate?.transactionsUpdated(
inserted: insertions.map { collection[$0] }.map { transactionInfo(fromTransaction: $0) },
updated: modifications.map { collection[$0] }.map { transactionInfo(fromTransaction: $0) },
deleted: deletions
)
balanceUpdateSubject.onNext(())
}
}

private func handleBlocks(changeset: RealmCollectionChange<Results<Block>>) {
if case let .update(collection, deletions, insertions, _) = changeset, let block = collection.last, (!deletions.isEmpty || !insertions.isEmpty) {
let blockInfo = self.blockInfo(fromBlock: block)
lastBlockInfo = blockInfo

delegate?.lastBlockInfoUpdated(lastBlockInfo: blockInfo)
balanceUpdateSubject.onNext(())
}
}

private var transactionRealmResults: Results<Transaction> {
return realmFactory.realm.objects(Transaction.self).filter("isMine = %@", true).sorted(byKeyPath: "block.height", ascending: false)
}

private var blockRealmResults: Results<Block> {
return realmFactory.realm.objects(Block.self).sorted(byKeyPath: "height")
}).disposed(by: disposeBag)
}

private func transactionInfo(fromTransaction transaction: Transaction) -> TransactionInfo {
Expand Down Expand Up @@ -144,6 +104,33 @@ class DataProvider {

}

extension DataProvider: IBlockchainDataListener {

func onUpdate(updated: [Transaction], inserted: [Transaction]) {
delegate?.transactionsUpdated(inserted: inserted.map { transactionInfo(fromTransaction: $0) },
updated: updated.map { transactionInfo(fromTransaction: $0) })

balanceUpdateSubject.onNext(())
}

func onDelete(transactionHashes: [String]) {
delegate?.transactionsDeleted(hashes: transactionHashes)

balanceUpdateSubject.onNext(())
}

func onInsert(block: Block) {
if block.height > (lastBlockInfo?.height ?? 0) {
let lastBlockInfo = blockInfo(fromBlock: block)
self.lastBlockInfo = lastBlockInfo
delegate?.lastBlockInfoUpdated(lastBlockInfo: lastBlockInfo)

balanceUpdateSubject.onNext(())
}
}

}

extension DataProvider: IDataProvider {

func transactions(fromHash: String?, limit: Int?) -> Single<[TransactionInfo]> {
Expand Down
18 changes: 13 additions & 5 deletions HSBitcoinKit/HSBitcoinKit/Core/KitStateProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@ class KitStateProvider: IKitStateProvider {
private var initialBestBlockHeight: Int32 = 0
private var currentBestBlockHeight: Int32 = 0

private(set) var syncState: BitcoinKit.KitState = .notSynced {
didSet {
if !(oldValue == syncState) {
delegate?.handleKitStateUpdate(state: syncState)
}
}
}

}

extension KitStateProvider: ISyncStateListener {

func syncStarted() {
delegate?.handleKitStateUpdate(state: BitcoinKit.KitState.syncing(progress: 0))
syncState = .syncing(progress: 0)
}

func syncStopped() {
delegate?.handleKitStateUpdate(state: BitcoinKit.KitState.notSynced)
syncState = .notSynced
}

func syncFinished() {
delegate?.handleKitStateUpdate(state: BitcoinKit.KitState.synced)
syncState = .synced
}

func initialBestBlockHeightUpdated(height: Int32) {
Expand All @@ -44,9 +52,9 @@ extension KitStateProvider: ISyncStateListener {
}

if progress >= 1 {
delegate?.handleKitStateUpdate(state: BitcoinKit.KitState.synced)
syncState = .synced
} else {
delegate?.handleKitStateUpdate(state: BitcoinKit.KitState.syncing(progress: progress))
syncState = .syncing(progress: progress)
}
}

Expand Down
Loading

0 comments on commit 0272c54

Please sign in to comment.