dropzone_ruby 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Drop Zone - An Anonymous Peer-To-Peer Local Contraband Marketplace.pdf +0 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +69 -0
- data/README.md +62 -0
- data/bin/dropzone +487 -0
- data/dropzone-screenshot.jpg +0 -0
- data/dropzone_ruby.gemspec +31 -0
- data/lib/blockrio_ext.rb +52 -0
- data/lib/dropzone/buyer.rb +21 -0
- data/lib/dropzone/command.rb +488 -0
- data/lib/dropzone/communication.rb +43 -0
- data/lib/dropzone/connection.rb +312 -0
- data/lib/dropzone/invoice.rb +23 -0
- data/lib/dropzone/item.rb +160 -0
- data/lib/dropzone/listing.rb +64 -0
- data/lib/dropzone/message_base.rb +178 -0
- data/lib/dropzone/payment.rb +36 -0
- data/lib/dropzone/profile.rb +86 -0
- data/lib/dropzone/record_base.rb +34 -0
- data/lib/dropzone/seller.rb +21 -0
- data/lib/dropzone/session.rb +161 -0
- data/lib/dropzone/state_accumulator.rb +39 -0
- data/lib/dropzone/version.rb +4 -0
- data/lib/dropzone_ruby.rb +14 -0
- data/lib/veto_checks.rb +74 -0
- data/spec/bitcoin_spec.rb +115 -0
- data/spec/buyer_profile_spec.rb +279 -0
- data/spec/buyer_spec.rb +109 -0
- data/spec/command_spec.rb +353 -0
- data/spec/config.yml +5 -0
- data/spec/invoice_spec.rb +129 -0
- data/spec/item_spec.rb +294 -0
- data/spec/lib/fake_connection.rb +97 -0
- data/spec/listing_spec.rb +150 -0
- data/spec/payment_spec.rb +152 -0
- data/spec/seller_profile_spec.rb +290 -0
- data/spec/seller_spec.rb +120 -0
- data/spec/session_spec.rb +303 -0
- data/spec/sham/buyer.rb +5 -0
- data/spec/sham/invoice.rb +5 -0
- data/spec/sham/item.rb +8 -0
- data/spec/sham/payment.rb +13 -0
- data/spec/sham/seller.rb +7 -0
- data/spec/spec_helper.rb +49 -0
- metadata +267 -0
data/spec/config.yml
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
require_relative 'sham/invoice'
|
4
|
+
require_relative 'sham/payment'
|
5
|
+
|
6
|
+
describe Dropzone::Invoice do
|
7
|
+
include_context 'globals'
|
8
|
+
|
9
|
+
describe "defaults" do
|
10
|
+
it "has accessors" do
|
11
|
+
invoice = Dropzone::Invoice.sham!(:build)
|
12
|
+
|
13
|
+
expect(invoice.expiration_in).to eq(6)
|
14
|
+
expect(invoice.amount_due).to eq(100_000_000)
|
15
|
+
expect(invoice.receiver_addr).to eq(test_pubkey)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "serialization" do
|
20
|
+
it "serializes to_transaction" do
|
21
|
+
expect(Dropzone::Invoice.sham!(:build).to_transaction).to eq({
|
22
|
+
tip: 20000, receiver_addr: test_pubkey,
|
23
|
+
data: "INCRTE\x01p\xFE\x00\xE1\xF5\x05\x01e\x06".force_encoding('ASCII-8BIT') })
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "database" do
|
28
|
+
after{ clear_blockchain! }
|
29
|
+
|
30
|
+
it ".save() and .find()" do
|
31
|
+
invoice_id = Dropzone::Invoice.sham!(:build).save!(test_privkey)
|
32
|
+
expect(invoice_id).to be_kind_of(String)
|
33
|
+
|
34
|
+
invoice = Dropzone::Invoice.find invoice_id
|
35
|
+
|
36
|
+
expect(invoice.expiration_in).to eq(6)
|
37
|
+
expect(invoice.amount_due).to eq(100_000_000)
|
38
|
+
expect(invoice.receiver_addr).to eq(test_pubkey)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "associations" do
|
43
|
+
# It's a bit obtuse that there can be support for multiple payments
|
44
|
+
# but this should nonetheless be support to aid with reputation analysis
|
45
|
+
it "has_many payments" do
|
46
|
+
invoice_id = Dropzone::Invoice.sham!(:build,
|
47
|
+
receiver_addr: TESTER2_PUBLIC_KEY).save!(test_privkey)
|
48
|
+
|
49
|
+
Dropzone::Payment.sham!(:build, invoice_txid: invoice_id,
|
50
|
+
description: 'abc', receiver_addr: test_pubkey ).save! TESTER2_PRIVATE_KEY
|
51
|
+
|
52
|
+
increment_block_height!
|
53
|
+
|
54
|
+
Dropzone::Payment.sham!(:build, invoice_txid: invoice_id,
|
55
|
+
description: 'xyz', receiver_addr: test_pubkey ).save! TESTER2_PRIVATE_KEY
|
56
|
+
|
57
|
+
invoice = Dropzone::Invoice.find invoice_id
|
58
|
+
expect(invoice.payments.length).to eq(2)
|
59
|
+
expect(invoice.payments.collect(&:description)).to eq(['xyz','abc'])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "validations" do
|
64
|
+
it "validates default build" do
|
65
|
+
expect(Dropzone::Invoice.sham!(:build).valid?).to eq(true)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "validates minimal invoice" do
|
69
|
+
invoice = Dropzone::Invoice.new receiver_addr: test_pubkey
|
70
|
+
|
71
|
+
expect(invoice.valid?).to eq(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "expiration_in must be numeric" do
|
75
|
+
invoice = Dropzone::Invoice.sham! expiration_in: 'abc'
|
76
|
+
|
77
|
+
expect(invoice.valid?).to eq(false)
|
78
|
+
expect(invoice.errors.count).to eq(2)
|
79
|
+
expect(invoice.errors.on(:expiration_in)).to eq(
|
80
|
+
['is not a number', "must be greater than or equal to 0"])
|
81
|
+
end
|
82
|
+
|
83
|
+
it "expiration_in must be gt 0" do
|
84
|
+
invoice = Dropzone::Invoice.sham! expiration_in: -1
|
85
|
+
|
86
|
+
expect(invoice.valid?).to eq(false)
|
87
|
+
expect(invoice.errors.count).to eq(1)
|
88
|
+
expect(invoice.errors.on(:expiration_in)).to eq(
|
89
|
+
['must be greater than or equal to 0'])
|
90
|
+
end
|
91
|
+
|
92
|
+
it "amount_due must be numeric" do
|
93
|
+
invoice = Dropzone::Invoice.sham! amount_due: 'abc'
|
94
|
+
|
95
|
+
expect(invoice.valid?).to eq(false)
|
96
|
+
expect(invoice.errors.count).to eq(2)
|
97
|
+
expect(invoice.errors.on(:amount_due)).to eq(
|
98
|
+
['is not a number', "must be greater than or equal to 0"])
|
99
|
+
end
|
100
|
+
|
101
|
+
it "amount_due must be gt 0" do
|
102
|
+
invoice = Dropzone::Invoice.sham! amount_due: -1
|
103
|
+
|
104
|
+
expect(invoice.valid?).to eq(false)
|
105
|
+
expect(invoice.errors.count).to eq(1)
|
106
|
+
expect(invoice.errors.on(:amount_due)).to eq(
|
107
|
+
['must be greater than or equal to 0'])
|
108
|
+
end
|
109
|
+
|
110
|
+
it "validates output address must be present" do
|
111
|
+
invoice = Dropzone::Invoice.sham! receiver_addr: nil
|
112
|
+
|
113
|
+
expect(invoice.valid?).to eq(false)
|
114
|
+
expect(invoice.errors.count).to eq(1)
|
115
|
+
expect(invoice.errors.on(:receiver_addr)).to eq(['is not present'])
|
116
|
+
end
|
117
|
+
|
118
|
+
it "declaration must not be addressed to self" do
|
119
|
+
id = Dropzone::Invoice.sham!(receiver_addr: test_pubkey).save! test_privkey
|
120
|
+
|
121
|
+
invoice = Dropzone::Invoice.find id
|
122
|
+
|
123
|
+
expect(invoice.valid?).to eq(false)
|
124
|
+
expect(invoice.errors.count).to eq(1)
|
125
|
+
expect(invoice.errors.on(:receiver_addr)).to eq(['matches sender_addr'])
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
data/spec/item_spec.rb
ADDED
@@ -0,0 +1,294 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
require_relative 'sham/item'
|
4
|
+
|
5
|
+
describe Dropzone::Item do
|
6
|
+
include_context 'globals'
|
7
|
+
|
8
|
+
describe "defaults" do
|
9
|
+
it "has accessors" do
|
10
|
+
item = Dropzone::Item.sham!(:build)
|
11
|
+
|
12
|
+
expect(item.description).to eq("Item Description")
|
13
|
+
expect(item.price_currency).to eq('BTC')
|
14
|
+
expect(item.price_in_units).to eq(100_000_000)
|
15
|
+
expect(item.expiration_in).to eq(6)
|
16
|
+
expect(item.latitude).to eq(51.500782)
|
17
|
+
expect(item.longitude).to eq(-0.124669)
|
18
|
+
expect(item.radius).to eq(1000)
|
19
|
+
expect(item.receiver_addr).to eq('mfZ1415XX782179875331XX1XXXXXgtzWu')
|
20
|
+
expect(Bitcoin.valid_address?(item.receiver_addr)).to be_truthy
|
21
|
+
expect(item.sender_addr).to eq(nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "burn addresses" do
|
26
|
+
it "supports 6 digit distances" do
|
27
|
+
[90, 0, -90, 51.500782,-51.500782].each do |lat|
|
28
|
+
[180, 0, -180, -0.124669,0.124669].each do |lon|
|
29
|
+
[9,8,5,2,0,101,11010,999999,100000].each do |radius|
|
30
|
+
addr = Dropzone::Item.sham!(:build, :radius => radius,
|
31
|
+
:latitude => lat, :longitude => lon).receiver_addr
|
32
|
+
|
33
|
+
/\AmfZ([0-9X]{9})([0-9X]{9})([0-9X]{6})/.match addr
|
34
|
+
|
35
|
+
expect(addr.length).to eq(34)
|
36
|
+
expect($1.tr('X','0').to_i).to eq(((lat+90) * 1_000_000).floor)
|
37
|
+
expect($2.tr('X','0').to_i).to eq(((lon+180) * 1_000_000).floor)
|
38
|
+
expect($3.tr('X','0').to_i).to eq(radius)
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "serialization" do
|
48
|
+
it "serializes to_transaction" do
|
49
|
+
expect(Dropzone::Item.sham!(:build).to_transaction).to eq({ tip: 20000,
|
50
|
+
receiver_addr: "mfZ1415XX782179875331XX1XXXXXgtzWu",
|
51
|
+
data: "ITCRTE\x01d\x10Item Description\x01c\x03BTC\x01p\xFE\x00\xE1\xF5\x05\x01e\x06".force_encoding('ASCII-8BIT') })
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "database" do
|
56
|
+
after{ clear_blockchain! }
|
57
|
+
|
58
|
+
it ".save() and .find()" do
|
59
|
+
item_id = Dropzone::Item.sham!(:build).save!(test_privkey)
|
60
|
+
|
61
|
+
expect(item_id).to be_kind_of(String)
|
62
|
+
|
63
|
+
item = Dropzone::Item.find(item_id)
|
64
|
+
|
65
|
+
expect(item.description).to eq("Item Description")
|
66
|
+
expect(item.price_currency).to eq('BTC')
|
67
|
+
expect(item.price_in_units).to eq(100_000_000)
|
68
|
+
expect(item.expiration_in).to eq(6)
|
69
|
+
expect(item.latitude).to eq(51.500782)
|
70
|
+
expect(item.longitude).to eq(-0.124669)
|
71
|
+
expect(item.radius).to eq(1000)
|
72
|
+
expect(item.receiver_addr).to eq('mfZ1415XX782179875331XX1XXXXXgtzWu')
|
73
|
+
expect(Bitcoin.valid_address?(item.receiver_addr)).to be_truthy
|
74
|
+
expect(item.sender_addr).to eq(test_pubkey)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "updates must be addressed to self" do
|
78
|
+
item_id = Dropzone::Item.sham!(:build).save!(test_privkey)
|
79
|
+
|
80
|
+
update_id = Dropzone::Item.new(create_txid: item_id,
|
81
|
+
description: 'xyz').save! test_privkey
|
82
|
+
|
83
|
+
update_item = Dropzone::Item.find update_id
|
84
|
+
|
85
|
+
expect(update_item.description).to eq("xyz")
|
86
|
+
expect(update_item.message_type).to eq('ITUPDT')
|
87
|
+
expect(update_item.sender_addr).to eq(test_pubkey)
|
88
|
+
expect(update_item.receiver_addr).to eq(test_pubkey)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "validations" do
|
93
|
+
it "validates default build" do
|
94
|
+
expect(Dropzone::Item.sham!(:build).valid?).to eq(true)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "validates minimal item" do
|
98
|
+
minimal_item = Dropzone::Item.new radius: 1, latitude: 51.500782,
|
99
|
+
longitude: -0.124669
|
100
|
+
|
101
|
+
expect(minimal_item.valid?).to eq(true)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "requires output address" do
|
105
|
+
no_address = Dropzone::Item.sham! latitude: nil, longitude: nil, radius: nil
|
106
|
+
|
107
|
+
expect(no_address.valid?).to eq(false)
|
108
|
+
expect(no_address.errors.count).to eq(4)
|
109
|
+
expect(no_address.errors.on(:receiver_addr)).to eq(['is not present'])
|
110
|
+
end
|
111
|
+
|
112
|
+
it "requires latitude" do
|
113
|
+
item = Dropzone::Item.sham! latitude: nil
|
114
|
+
|
115
|
+
expect(item.valid?).to eq(false)
|
116
|
+
expect(item.errors.count).to eq(2)
|
117
|
+
expect(item.errors.on(:latitude)).to eq(['is not a number'])
|
118
|
+
end
|
119
|
+
|
120
|
+
it "requires latitude is gte -90" do
|
121
|
+
item = Dropzone::Item.sham! latitude: -90.000001
|
122
|
+
|
123
|
+
expect(item.valid?).to eq(false)
|
124
|
+
expect(item.errors.count).to eq(1)
|
125
|
+
expect(item.errors.on(:latitude)).to eq(['must be greater than or equal to -90'])
|
126
|
+
end
|
127
|
+
|
128
|
+
it "requires latitude is lte 90" do
|
129
|
+
item = Dropzone::Item.sham! latitude: 90.000001
|
130
|
+
|
131
|
+
expect(item.valid?).to eq(false)
|
132
|
+
expect(item.errors.count).to eq(1)
|
133
|
+
expect(item.errors.on(:latitude)).to eq(['must be less than or equal to 90'])
|
134
|
+
end
|
135
|
+
|
136
|
+
it "requires longitude" do
|
137
|
+
item = Dropzone::Item.sham! longitude: nil
|
138
|
+
|
139
|
+
expect(item.valid?).to eq(false)
|
140
|
+
expect(item.errors.count).to eq(2)
|
141
|
+
expect(item.errors.on(:longitude)).to eq(['is not a number'])
|
142
|
+
end
|
143
|
+
|
144
|
+
it "requires longitude is gte -180" do
|
145
|
+
item = Dropzone::Item.sham! longitude: -180.000001
|
146
|
+
|
147
|
+
expect(item.valid?).to eq(false)
|
148
|
+
expect(item.errors.count).to eq(1)
|
149
|
+
expect(item.errors.on(:longitude)).to eq(['must be greater than or equal to -180'])
|
150
|
+
end
|
151
|
+
|
152
|
+
it "requires longitude is lte 180" do
|
153
|
+
item = Dropzone::Item.sham! longitude: 180.000001
|
154
|
+
|
155
|
+
expect(item.valid?).to eq(false)
|
156
|
+
expect(item.errors.count).to eq(1)
|
157
|
+
expect(item.errors.on(:longitude)).to eq(['must be less than or equal to 180'])
|
158
|
+
end
|
159
|
+
|
160
|
+
it "requires radius" do
|
161
|
+
item = Dropzone::Item.sham! radius: nil
|
162
|
+
|
163
|
+
expect(item.valid?).to eq(false)
|
164
|
+
expect(item.errors.count).to eq(2)
|
165
|
+
expect(item.errors.on(:radius)).to eq(['is not a number'])
|
166
|
+
end
|
167
|
+
|
168
|
+
it "requires radius is gte 0" do
|
169
|
+
item = Dropzone::Item.sham! radius: -1
|
170
|
+
|
171
|
+
expect(item.valid?).to eq(false)
|
172
|
+
expect(item.errors.count).to eq(1)
|
173
|
+
expect(item.errors.on(:radius)).to eq(['must be greater than or equal to 0'])
|
174
|
+
end
|
175
|
+
|
176
|
+
it "requires radius is lt 1000000" do
|
177
|
+
item = Dropzone::Item.sham! radius: 1000000
|
178
|
+
|
179
|
+
expect(item.valid?).to eq(false)
|
180
|
+
expect(item.errors.count).to eq(1)
|
181
|
+
expect(item.errors.on(:radius)).to eq(['must be less than 1000000'])
|
182
|
+
end
|
183
|
+
|
184
|
+
it "requires message_type" do
|
185
|
+
item = Dropzone::Item.sham! message_type: 'INVALD'
|
186
|
+
|
187
|
+
expect(item.valid?).to eq(false)
|
188
|
+
expect(item.errors.count).to eq(1)
|
189
|
+
expect(item.errors.on(:message_type)).to eq(['is not valid'])
|
190
|
+
end
|
191
|
+
|
192
|
+
it "descriptions must be text" do
|
193
|
+
item = Dropzone::Item.sham! description: 5
|
194
|
+
|
195
|
+
expect(item.valid?).to eq(false)
|
196
|
+
expect(item.errors.count).to eq(1)
|
197
|
+
expect(item.errors.on(:description)).to eq(['is not a string'])
|
198
|
+
end
|
199
|
+
|
200
|
+
it "price_in_units must be numeric" do
|
201
|
+
item = Dropzone::Item.sham! price_in_units: 'abc',
|
202
|
+
price_currency: 'USD'
|
203
|
+
|
204
|
+
expect(item.valid?).to eq(false)
|
205
|
+
expect(item.errors.count).to eq(2)
|
206
|
+
expect(item.errors.on(:price_in_units)).to eq(['is not a number',
|
207
|
+
"must be greater than or equal to 0"])
|
208
|
+
end
|
209
|
+
|
210
|
+
it "expiration_in must be numeric" do
|
211
|
+
item = Dropzone::Item.sham! expiration_in: 'abc'
|
212
|
+
|
213
|
+
expect(item.valid?).to eq(false)
|
214
|
+
expect(item.errors.count).to eq(2)
|
215
|
+
expect(item.errors.on(:expiration_in)).to eq(['is not a number',
|
216
|
+
"must be greater than or equal to 0"])
|
217
|
+
end
|
218
|
+
|
219
|
+
it "price_currency must be present if price is present" do
|
220
|
+
item = Dropzone::Item.sham! price_in_units: 100, price_currency: nil
|
221
|
+
|
222
|
+
expect(item.valid?).to eq(false)
|
223
|
+
expect(item.errors.count).to eq(1)
|
224
|
+
expect(item.errors.on(:price_currency)).to eq(['is required if price is specified'])
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "distance calculations" do
|
230
|
+
it "calculates distance in meters between two points" do
|
231
|
+
# New York to London:
|
232
|
+
nyc_to_london = Dropzone::Item.distance_between 40.712784, -74.005941,
|
233
|
+
51.507351, -0.127758
|
234
|
+
texas = Dropzone::Item.distance_between 31.428663, -99.096680,
|
235
|
+
36.279707, -102.568359
|
236
|
+
hong_kong = Dropzone::Item.distance_between 22.396428, 114.109497,
|
237
|
+
22.408489, 113.906937
|
238
|
+
|
239
|
+
expect(nyc_to_london.round).to eq(5570224)
|
240
|
+
expect(texas.round).to eq(627363)
|
241
|
+
expect(hong_kong.round).to eq(20867)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe 'finders' do
|
246
|
+
after{ clear_blockchain! }
|
247
|
+
|
248
|
+
before do
|
249
|
+
# < 20 km from shinjuku
|
250
|
+
fuchu_id = Dropzone::Item.sham!(:build, :description => 'Fuchu',
|
251
|
+
:radius => 20_000, :latitude => 35.688533,
|
252
|
+
:longitude => 139.471436).save! test_privkey
|
253
|
+
|
254
|
+
increment_block_height!
|
255
|
+
|
256
|
+
# 36 km from shinjuku
|
257
|
+
Dropzone::Item.sham!(:build, :description => 'Abiko', :radius => 20_000,
|
258
|
+
:latitude => 35.865683, :longitude => 140.031738).save! TESTER2_PRIVATE_KEY
|
259
|
+
|
260
|
+
# 3 km from shinjuku
|
261
|
+
Dropzone::Item.sham!(:build, :description => 'Nakano', :radius => 20_000,
|
262
|
+
:latitude => 35.708050, :longitude => 139.664383).save! TESTER3_PRIVATE_KEY
|
263
|
+
|
264
|
+
increment_block_height!
|
265
|
+
|
266
|
+
# 38.5 km from shinjuku
|
267
|
+
Dropzone::Item.sham!(:build, :description => 'Chiba', :radius => 20_000,
|
268
|
+
:latitude => 35.604835, :longitude => 140.105209).save! test_privkey
|
269
|
+
|
270
|
+
# This shouldn't actually be returned, since it's an update, and
|
271
|
+
# find_creates_since_block only looks for creates:
|
272
|
+
Dropzone::Item.new(create_txid: fuchu_id,
|
273
|
+
description: 'xyz').save! test_privkey
|
274
|
+
end
|
275
|
+
|
276
|
+
it ".find_creates_since_block()" do
|
277
|
+
items = Dropzone::Item.find_creates_since_block block_height, block_height
|
278
|
+
|
279
|
+
expect(items.length).to eq(4)
|
280
|
+
expect(items.collect(&:description)).to eq(['Chiba', 'Nakano', 'Abiko',
|
281
|
+
'Fuchu'])
|
282
|
+
end
|
283
|
+
|
284
|
+
it ".find_in_radius()" do
|
285
|
+
# Twenty km around Shinjuku:
|
286
|
+
items = Dropzone::Item.find_in_radius block_height, block_height,
|
287
|
+
35.689487, 139.691706, 20_000
|
288
|
+
expect(items.length).to eq(2)
|
289
|
+
expect(items.collect(&:description)).to eq(['Nakano', 'Fuchu'])
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
class FakeBitcoinConnection
|
4
|
+
attr_accessor :height, :transactions
|
5
|
+
|
6
|
+
DB ||= Sequel.sqlite # logger: Logger.new(STDOUT)
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
DB.create_table :transactions do
|
10
|
+
primary_key :id
|
11
|
+
File :data
|
12
|
+
String :receiver_addr
|
13
|
+
String :sender_addr
|
14
|
+
Integer :tip
|
15
|
+
Integer :block_height
|
16
|
+
end unless DB.table_exists?(:transactions)
|
17
|
+
|
18
|
+
@height = 0
|
19
|
+
@transactions = DB[:transactions]
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_testing?; true; end
|
23
|
+
def privkey_to_addr(key); Bitcoin::Key.from_base58(key).addr; end
|
24
|
+
def hash160_to_address(hash160); Bitcoin.hash160_to_address hash160; end
|
25
|
+
def hash160_from_address(addr); Bitcoin.hash160_from_address addr; end
|
26
|
+
def valid_address?(addr); Bitcoin.valid_address? addr; end
|
27
|
+
|
28
|
+
# NOTE:
|
29
|
+
# - This needs to return the messages in Descending order by block
|
30
|
+
# In the case that two transactions are in the same block, it goes by time
|
31
|
+
# - This should return only 'valid' messages. Not all transactions
|
32
|
+
def messages_by_addr(addr, options = {})
|
33
|
+
filter_messages transactions.where(
|
34
|
+
Sequel.expr(receiver_addr: addr) | Sequel.expr(sender_addr: addr) ),
|
35
|
+
options
|
36
|
+
end
|
37
|
+
|
38
|
+
def messages_in_block(block_height, options = {})
|
39
|
+
filter_messages transactions.where(
|
40
|
+
Sequel.expr(block_height: block_height) ), options
|
41
|
+
end
|
42
|
+
|
43
|
+
def tx_by_id(id)
|
44
|
+
record_to_tx transactions[id: id.to_i]
|
45
|
+
end
|
46
|
+
|
47
|
+
# We ignore the private key in this connection. We return the database id
|
48
|
+
# in lieue of transaction id.
|
49
|
+
def save!(tx, private_key)
|
50
|
+
transactions.insert(tx.tap{ |et|
|
51
|
+
et[:block_height] = @height
|
52
|
+
et[:sender_addr] = privkey_to_addr(private_key)
|
53
|
+
et[:data] = Sequel.blob et[:data]
|
54
|
+
}).to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
# This aids test mode:
|
58
|
+
def clear_transactions!
|
59
|
+
transactions.delete
|
60
|
+
@height = 0
|
61
|
+
end
|
62
|
+
|
63
|
+
def increment_block_height!
|
64
|
+
@height += 1
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def record_to_tx(record)
|
70
|
+
record.tap{|r| r[:txid] = r.delete(:id).to_s } if record
|
71
|
+
end
|
72
|
+
|
73
|
+
def filter_messages(messages, options = {})
|
74
|
+
if options.has_key?(:start_block)
|
75
|
+
messages = messages.where{block_height >= options[:start_block]}
|
76
|
+
end
|
77
|
+
if options.has_key?(:end_block)
|
78
|
+
messages = messages.where{block_height <= options[:end_block]}
|
79
|
+
end
|
80
|
+
|
81
|
+
ret = messages.order(Sequel.desc(:block_height)).order(Sequel.desc(:id)).to_a
|
82
|
+
ret = ret.collect{ |tx|
|
83
|
+
msg = Dropzone::MessageBase.new_message_from record_to_tx(tx)
|
84
|
+
msg.valid? ? msg : nil
|
85
|
+
}.compact
|
86
|
+
|
87
|
+
ret = ret.find_all{|msg| msg.message_type == options[:type]} if ret && options.has_key?(:type)
|
88
|
+
|
89
|
+
if options.has_key?(:between)
|
90
|
+
ret = ret.find_all{|c|
|
91
|
+
[c.receiver_addr, c.sender_addr].all?{|a| options[:between].include?(a) } }
|
92
|
+
end
|
93
|
+
|
94
|
+
(ret) ? ret : []
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|