dropzone_ruby 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|