Skip to content

Commit 3e7dd24

Browse files
author
swiftbit
committed
Initial commit
1 parent 0919efd commit 3e7dd24

File tree

974 files changed

+128783
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

974 files changed

+128783
-2
lines changed

ArcBit.xcodeproj/project.pbxproj

Lines changed: 1638 additions & 0 deletions
Large diffs are not rendered by default.

ArcBit.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ArcBit.xcworkspace/contents.xcworkspacedata

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
//
2+
// TLBitcoinListener.swift
3+
// ArcBit
4+
//
5+
// Created by Timothy Lee on 3/14/15.
6+
// Copyright (c) 2015 Timothy Lee <[email protected]>
7+
//
8+
// This library is free software; you can redistribute it and/or
9+
// modify it under the terms of the GNU Lesser General Public
10+
// License as published by the Free Software Foundation; either
11+
// version 2.1 of the License, or (at your option) any later version.
12+
//
13+
// This library is distributed in the hope that it will be useful,
14+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
// Lesser General Public License for more details.
17+
//
18+
// You should have received a copy of the GNU Lesser General Public
19+
// License along with this library; if not, write to the Free Software
20+
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21+
// MA 02110-1301 USA
22+
23+
import Foundation
24+
25+
@objc class TLTransactionListener: NSObject, SRWebSocketDelegate {
26+
let MAX_CONSECUTIVE_FAILED_CONNECTIONS = 5
27+
let SEND_EMPTY_PACKET_TIME_INTERVAL = 60.0
28+
private var blockExplorerAPI: TLBlockExplorer?
29+
private var keepAliveTimer: NSTimer?
30+
private var socket: SIOSocket?
31+
private var socketIsConnected: Bool = false
32+
private var webSocket: SRWebSocket?
33+
var consecutiveFailedConnections = 0
34+
35+
struct STATIC_MEMBERS {
36+
static var instance: TLTransactionListener?
37+
}
38+
39+
class func instance() -> (TLTransactionListener) {
40+
if (STATIC_MEMBERS.instance == nil) {
41+
STATIC_MEMBERS.instance = TLTransactionListener()
42+
}
43+
return STATIC_MEMBERS.instance!
44+
}
45+
46+
override init() {
47+
super.init()
48+
blockExplorerAPI = TLPreferences.getBlockExplorerAPI()
49+
}
50+
51+
func reconnect() -> () {
52+
if (blockExplorerAPI == TLBlockExplorer.Blockchain) {
53+
DLog("websocket reconnect blockchain.info")
54+
if (self.webSocket != nil) {
55+
self.webSocket!.delegate = nil
56+
self.webSocket!.close()
57+
}
58+
59+
self.webSocket = SRWebSocket(URLRequest: NSURLRequest(URL: NSURL(string: "wss://ws.blockchain.info/inv")!))
60+
61+
self.webSocket!.delegate = self
62+
63+
self.webSocket!.open()
64+
} else {
65+
DLog("websocket reconnect insight")
66+
let url = String(format: "%@", TLPreferences.getBlockExplorerURL(TLBlockExplorer.Insight)!)
67+
SIOSocket.socketWithHost(url, response: {
68+
(socket: SIOSocket!) in
69+
self.socket = socket
70+
71+
weak var weakSelf = self
72+
self.socket!.onConnect = {
73+
() in
74+
DLog("socketio onConnect")
75+
self.consecutiveFailedConnections = 0
76+
weakSelf!.socketIsConnected = true
77+
NSNotificationCenter.defaultCenter().postNotificationName(TLNotificationEvents.EVENT_TRANSACTION_LISTENER_OPEN(), object: nil, userInfo: nil)
78+
}
79+
80+
self.socket!.onDisconnect = {
81+
() in
82+
DLog("socketio onDisconnect")
83+
NSNotificationCenter.defaultCenter().postNotificationName(TLNotificationEvents.EVENT_TRANSACTION_LISTENER_CLOSE(), object: nil, userInfo: nil)
84+
if self.consecutiveFailedConnections++ < self.MAX_CONSECUTIVE_FAILED_CONNECTIONS {
85+
self.socket?.close()
86+
self.reconnect()
87+
}
88+
}
89+
90+
self.socket!.onError = {
91+
(data: [NSObject:AnyObject]!) in
92+
DLog("socketio error: %@", data)
93+
}
94+
95+
self.socket!.onReconnectionAttempt = {
96+
(recCounter: Int) in
97+
DLog("socketio Reconnection counter \(recCounter)")
98+
}
99+
100+
self.socket!.onReconnectionError = {
101+
(data: [NSObject:AnyObject]!) in
102+
DLog("socketio reconnection error: %@", data)
103+
}
104+
105+
self.socket!.on("block", callback: {
106+
(_args: [AnyObject]!) in
107+
let args = _args as NSArray
108+
let data: AnyObject? = args.firstObject
109+
// data!.debugDescription is lastest block hash
110+
// can't use this to update confirmations on transactions because insight tx does not contain blockheight field
111+
DLog("socketio received lastest block hash: %@", data!.debugDescription ?? "")
112+
})
113+
})
114+
}
115+
}
116+
117+
func isWebSocketOpen() -> Bool {
118+
if (blockExplorerAPI == TLBlockExplorer.Blockchain) {
119+
return self.webSocket != nil && self.webSocket!.readyState.value == SR_OPEN.value
120+
} else {
121+
return self.socketIsConnected
122+
}
123+
}
124+
125+
private func sendWebSocketMessage(msg: String) -> Bool {
126+
DLog("sendWebSocketMessage msg: %@", msg)
127+
if self.isWebSocketOpen() {
128+
self.webSocket!.send(msg)
129+
return true
130+
} else {
131+
DLog("Websocket Error: not connect to websocket server")
132+
return false
133+
}
134+
}
135+
136+
func listenToIncomingTransactionForAddress(address: String) -> Bool {
137+
//DLog("listen address: %@", address)
138+
if (blockExplorerAPI == TLBlockExplorer.Blockchain) {
139+
if self.isWebSocketOpen() {
140+
let msg = String(format: "{\"op\":\"addr_sub\", \"addr\":\"%@\"}", address)
141+
self.sendWebSocketMessage(msg)
142+
return true
143+
} else {
144+
DLog("Websocket Error: not connect to websocket server")
145+
return false
146+
}
147+
} else {
148+
if (self.socketIsConnected) {
149+
if self.socket == nil {
150+
return false
151+
}
152+
self.socket!.emit("subscribe", args: [address])
153+
154+
self.socket!.on(address, callback: {
155+
(_args: [AnyObject]!) in
156+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
157+
158+
let args = _args as NSArray
159+
let data: AnyObject? = args.firstObject
160+
161+
let txHash = data!.debugDescription ?? ""
162+
DLog("socketio on address: %@", address)
163+
DLog("socketio transaction: %@", txHash)
164+
165+
//TODO: temp solution, ask Insight devs to get all tx data in websockets API
166+
TLBlockExplorerAPI.instance().getTx(txHash, success: {
167+
(txDict: AnyObject!) in
168+
NSNotificationCenter.defaultCenter().postNotificationName(TLNotificationEvents.EVENT_NEW_UNCONFIRMED_TRANSACTION(), object: txDict, userInfo: nil)
169+
170+
}, failure: {
171+
(code: NSInteger, status: String!) in
172+
})
173+
}
174+
})
175+
return true
176+
177+
} else {
178+
return false
179+
}
180+
}
181+
}
182+
183+
func close() -> () {
184+
if (blockExplorerAPI == TLBlockExplorer.Blockchain) {
185+
DLog("closing blockchain.info websocket")
186+
self.webSocket!.close()
187+
} else {
188+
DLog("closing socketio")
189+
self.socket!.close()
190+
}
191+
}
192+
193+
private func keepAlive() -> () {
194+
if (keepAliveTimer != nil) {
195+
keepAliveTimer!.invalidate()
196+
}
197+
keepAliveTimer = nil
198+
keepAliveTimer = NSTimer.scheduledTimerWithTimeInterval(SEND_EMPTY_PACKET_TIME_INTERVAL,
199+
target: self,
200+
selector: "sendEmptyPacket",
201+
userInfo: nil,
202+
repeats: true)
203+
}
204+
205+
func sendEmptyPacket() -> () {
206+
DLog("blockchain.info Websocket sendEmptyPacket")
207+
if self.isWebSocketOpen() {
208+
self.sendWebSocketMessage("")
209+
}
210+
}
211+
212+
func webSocketDidOpen(webSocket: SRWebSocket) -> () {
213+
DLog("blockchain.info webSocketDidOpen")
214+
consecutiveFailedConnections = 0
215+
self.sendWebSocketMessage("{\"op\":\"blocks_sub\"}")
216+
217+
self.keepAlive()
218+
219+
NSNotificationCenter.defaultCenter().postNotificationName(TLNotificationEvents.EVENT_TRANSACTION_LISTENER_OPEN(), object: nil, userInfo: nil)
220+
}
221+
222+
func webSocket(webSocket:SRWebSocket, didFailWithError error:NSError) -> () {
223+
DLog("blockchain.info Websocket didFailWithError %@", error.description)
224+
225+
self.webSocket!.delegate = nil
226+
self.webSocket!.close()
227+
self.webSocket = nil
228+
if consecutiveFailedConnections++ < MAX_CONSECUTIVE_FAILED_CONNECTIONS {
229+
self.reconnect()
230+
}
231+
}
232+
233+
func webSocket(webSocket: SRWebSocket, didReceiveMessage message: AnyObject) {
234+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
235+
var data = message.dataUsingEncoding(NSUTF8StringEncoding)
236+
237+
var error: NSError?
238+
var jsonDict = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions(0), error: &error) as! NSDictionary
239+
DLog("blockchain.info didReceiveMessage \(jsonDict.description)")
240+
241+
if (jsonDict.objectForKey("op") as! String == "utx") {
242+
NSNotificationCenter.defaultCenter().postNotificationName(TLNotificationEvents.EVENT_NEW_UNCONFIRMED_TRANSACTION(), object: jsonDict.objectForKey("x"), userInfo: nil)
243+
} else if (jsonDict.objectForKey("op") as! String == "block") {
244+
NSNotificationCenter.defaultCenter().postNotificationName(TLNotificationEvents.EVENT_NEW_BLOCK(), object: jsonDict.objectForKey("x"), userInfo: nil)
245+
}
246+
}
247+
}
248+
249+
func webSocket(webSocket: SRWebSocket, didCloseWithCode code: Int, reason: String, wasClean: Bool) -> () {
250+
if wasClean {
251+
DLog("blockchain.info Websocket didCloseWithCode With No Error \(code) \(reason)")
252+
} else {
253+
DLog("blockchain.info Websocket didCloseWithCode With Error \(code) \(reason)")
254+
}
255+
256+
self.webSocket!.delegate = nil
257+
self.webSocket!.close()
258+
self.webSocket = nil
259+
if consecutiveFailedConnections++ < MAX_CONSECUTIVE_FAILED_CONNECTIONS {
260+
self.reconnect()
261+
}
262+
NSNotificationCenter.defaultCenter().postNotificationName(TLNotificationEvents.EVENT_TRANSACTION_LISTENER_CLOSE(), object: nil, userInfo: nil)
263+
}
264+
}

0 commit comments

Comments
 (0)