-
Notifications
You must be signed in to change notification settings - Fork 4
/
bitshovel.js
231 lines (212 loc) · 7.32 KB
/
bitshovel.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//ALERT: This software is alpha. Use at your own risk, especially if using mainnet
//Always, Always, Always protect your private key!
//This the connection point between bitcoin and your local application
//implemented with unwriter libraries for reading and writing to bitcoin
const utils = require('./utils')
const shovelwallet = require('./shovelwallet')
const shoveladdress = require('./shoveladdress')
const shovelcache = require('./shovelcache')
//application registry
const shovelregistry = require('./shovelregistry')
//bitsocket listeners
const shovellisteners = require('./shovellisteners')
const localbus = require('./shovelbus')
//for writing to bitcoin
const datapay = require('datapay');
const fs = require('fs');
const axios = require('axios')
const DEBUG = false
//eventually these items will be configurable
const BITDB_SOURCE = 'https://bitdb.network/q/'
const BITDB_API_KEY = 'qrr6u2amzamlf7cay750j0fnd76fhhcpxgr2ekv2wg'
//create a wallet if there is none
if (!fs.existsSync(shovelwallet.walletFileName)) {
shovelwallet.generateWallet();
}
let wallet = require(`./${shovelwallet.walletFileName}`);
function main () {
localbus.start()
shovelcache.start()
shovelcache.storeWalletAddress(wallet.address);
localbus.sub.on("message", function (channel, message) {
try {
console.log(`got message from local bus ${channel}: ${message}`);
if (channel === localbus.CHANNEL_SEND) {
//send the message to bitcoin
shovelToBitcoin(message);
}
//example: bitshovel.stream start|stop name query
if (channel === localbus.CHANNEL_STREAM) {
const cmd = utils.parseCommand("stream", message)
if (utils.isStart(cmd.action)) {
//start listening to bitsocket messages
//this will start broadcasting bitcoin tx on to your local bus
console.log(`starting bitsocket listener ${message}`);
startBitSocket(cmd);
}
if (utils.isStop(cmd.action)) {
shovellisteners.close(cmd.name)
}
}
if (channel === localbus.CHANNEL_APP) {
//application (address) level command. Assumes an address is specified
//bitsocket used cashaddr. will be converted to use legacy address soon
const cmd = utils.parseCommand("app", message)
console.log(cmd)
if (utils.isStart(cmd.action)) {
console.log(`starting app listener ${message}`);
startBitSocket(cmd);
} else if (utils.isStop(cmd.action)) {
stopBitSocket(cmd);
} else if (cmd.action === 'register') {
registerApplication(cmd)
} else {
console.log(`unknown command ${cmd}`)
}
}
if (channel === localbus.CHANNEL_QUERY) {
const url = utils.urlString(BITDB_SOURCE, makeBitquery(JSON.parse(message)))
getBitdb(url)
}
if (channel === localbus.CHANNEL_WALLET) {
//store the private key in local wallet
//TODO:should encrypt private key on the wire
if (wallet.wif != message) {
wallet = shovelwallet.generateWallet(message);
shovelcache.storeWalletAddress(wallet.address);
}
}
}
catch (err) {
console.error(err)
}
})
localbus.subscribeall()
}
main()
function registerApplication(cmd) {
//for now use the bitshovel wallet, in future probably want each app to have wallet
localbus.pub.set(cmd.name, wallet.address, function(err) {
if (err) {
console.error(err)
}
})
//localbus_pub.hmset(CHANNEL_APP, cmd.name, wallet.address)
//register the app in the app registry
shovelregistry.register(cmd.name, wallet.address)
}
function getBitdb(url) {
var header = {
headers: { key: BITDB_API_KEY }
}
axios.get(url, header).then(function(r) {
//console.log(`queried ${r.data.u.length} results`)
//unconfirmed tx
for (let item of r.data.u) {
localbus.publish(JSON.stringify(item))
}
//confirmed tx
for (let item of r.data.c) {
localbus.publish(JSON.stringify(item))
}
})
}
//start listening for bitcoin messages
//example query...
// {"v": 3, "q": { "find": {} }}
function startBitSocket(cmd) {
//let address = findAddressforApplication(cmd)
let query = queryFromCommand(cmd)
const listener = shovellisteners.listen(cmd.name, query)
listener.bitsocket.onmessage = function(e) {
//surprising that this works! function keeps the listener name in context when it fires
console.log(listener.name);
logTna(e);
localbus.publish(e.data);
}
listener.bitsocket.onerror = function(err) {
console.log(err);
}
}
function logit(category, s) {
console.log(`${category} ${s}`)
}
function logTna(tna) {
console.log(tna);
}
function queryFromCommand(cmd) {
if (cmd.command === "stream") {
//parameter is the query
return makeBitquery(JSON.parse(cmd.parameter))
}
if (cmd.command === "app") {
//parameter is the address to monitor
address = findAddressforApplication(cmd)
console.log(address)
const qry = {
"v": 3,
"q": {
"find": {
"out.e.a": shoveladdress.toCashAddress(address)
}
}
}
return makeBitquery(qry)
}
//what to do?
return cmd.parameter
}
//translate application alias to address
function findAddressforApplication(cmd) {
// (async () => {
//if param was passed then use that as address
if (cmd.parameter) {
return cmd.parameter
}
//otherwise look up the app by name
// await localbus_pub.hget(CHANNEL_APP, cmd.name, function (err, obj) {
// if (obj) {
// return obj
// }
// return cmd.name
// })
if (shovelregistry.has(cmd.name)) {
return shovelregistry.get(cmd.name)
}
return cmd.name
// })()
}
//make a standard bitquery query
//basically it will add any missing attributes to make it a standard query
function makeBitquery(query) {
let fullquery = query
if (!query.hasOwnProperty("q")) {
//assume that query is just the q attribute, fill in the rest
fullquery = {
"v": 3,
"q": query
}
}
return JSON.stringify(fullquery)
}
//write the message to bitcoin by creating a transaction
function shovelToBitcoin(message) {
//TODO: for now assume ascii text, later need to distinguish from hex string
//console.log(`shoveling to bitcoin >${message}<`);
let split = utils.splitString(message)
console.log(split)
if (!DEBUG) {
datapay.send({
data: split,
pay: { key: wallet.wif }
}, function sendResult(err,txid) {
if (err) {
console.log(err);
}
if (txid) {
console.log(`txid:${txid}`);
}
});
}
//TODO:could raise event saying that tx was sent
}