dropzone_ruby 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/Drop Zone - An Anonymous Peer-To-Peer Local Contraband Marketplace.pdf +0 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +69 -0
  6. data/README.md +62 -0
  7. data/bin/dropzone +487 -0
  8. data/dropzone-screenshot.jpg +0 -0
  9. data/dropzone_ruby.gemspec +31 -0
  10. data/lib/blockrio_ext.rb +52 -0
  11. data/lib/dropzone/buyer.rb +21 -0
  12. data/lib/dropzone/command.rb +488 -0
  13. data/lib/dropzone/communication.rb +43 -0
  14. data/lib/dropzone/connection.rb +312 -0
  15. data/lib/dropzone/invoice.rb +23 -0
  16. data/lib/dropzone/item.rb +160 -0
  17. data/lib/dropzone/listing.rb +64 -0
  18. data/lib/dropzone/message_base.rb +178 -0
  19. data/lib/dropzone/payment.rb +36 -0
  20. data/lib/dropzone/profile.rb +86 -0
  21. data/lib/dropzone/record_base.rb +34 -0
  22. data/lib/dropzone/seller.rb +21 -0
  23. data/lib/dropzone/session.rb +161 -0
  24. data/lib/dropzone/state_accumulator.rb +39 -0
  25. data/lib/dropzone/version.rb +4 -0
  26. data/lib/dropzone_ruby.rb +14 -0
  27. data/lib/veto_checks.rb +74 -0
  28. data/spec/bitcoin_spec.rb +115 -0
  29. data/spec/buyer_profile_spec.rb +279 -0
  30. data/spec/buyer_spec.rb +109 -0
  31. data/spec/command_spec.rb +353 -0
  32. data/spec/config.yml +5 -0
  33. data/spec/invoice_spec.rb +129 -0
  34. data/spec/item_spec.rb +294 -0
  35. data/spec/lib/fake_connection.rb +97 -0
  36. data/spec/listing_spec.rb +150 -0
  37. data/spec/payment_spec.rb +152 -0
  38. data/spec/seller_profile_spec.rb +290 -0
  39. data/spec/seller_spec.rb +120 -0
  40. data/spec/session_spec.rb +303 -0
  41. data/spec/sham/buyer.rb +5 -0
  42. data/spec/sham/invoice.rb +5 -0
  43. data/spec/sham/item.rb +8 -0
  44. data/spec/sham/payment.rb +13 -0
  45. data/spec/sham/seller.rb +7 -0
  46. data/spec/spec_helper.rb +49 -0
  47. metadata +267 -0
@@ -0,0 +1,39 @@
1
+ module Dropzone
2
+ module StateAccumulator
3
+ # NOTE: This includes only the valid() messages sent/received on this address
4
+ def messages(options = {})
5
+ @messages ||= blockchain.messages_by_addr addr,
6
+ {type: self.class.message_types}.merge(options)
7
+ end
8
+
9
+ def blockchain
10
+ Dropzone::RecordBase.blockchain
11
+ end
12
+
13
+ def self.included(base)
14
+ base.extend ClassMethods
15
+ end
16
+
17
+ module ClassMethods
18
+ def state_attr(*attrs)
19
+ attr_reader *attrs
20
+ @state_attributes ||= []
21
+ @state_attributes += attrs
22
+ end
23
+
24
+ def message_types=(type); @message_types = type; end
25
+ def message_types; @message_types; end
26
+ def state_attributes; @state_attributes; end
27
+ end
28
+
29
+ private
30
+
31
+ def attrs_from(message)
32
+ self.class.state_attributes.each do |attr|
33
+ value = message.send attr
34
+ self.instance_variable_set '@%s' % attr, value if value
35
+ end
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,4 @@
1
+ module Dropzone
2
+ # The library version string
3
+ VERSION = "0.1"
4
+ end
@@ -0,0 +1,14 @@
1
+ require 'bigdecimal'
2
+ require 'securerandom'
3
+
4
+ require 'veto'
5
+ require 'bitcoin'
6
+ require 'counterparty_ruby'
7
+
8
+ require 'veto_checks'
9
+ require 'blockrio_ext'
10
+
11
+ %w(connection record_base message_base state_accumulator item invoice payment
12
+ seller buyer profile listing communication session).each do |resource|
13
+ require 'dropzone/%s' % resource
14
+ end
@@ -0,0 +1,74 @@
1
+ module Veto
2
+ module AgainstAttributeCco
3
+ MSG_MISSING_ATTR = "missing required option :attribute"
4
+
5
+ def call(cco)
6
+ raise StandardError, MSG_MISSING_ATTR unless @options[:attribute]
7
+
8
+ value = cco.entity.public_send(@attribute_name)
9
+ value_against = cco.entity.public_send(@options[:attribute])
10
+
11
+ check @attribute_name, value, value_against, cco.errors, @options
12
+ end
13
+ end
14
+
15
+ class IsStringCheck < AttributeCheck
16
+ MSG = "is not a string"
17
+
18
+ def check(attribute, value, errors, options={})
19
+ on = options.fetch(:on, attribute)
20
+ errors.add(on, options[:message] || MSG) unless value.is_a? String
21
+ end
22
+ end
23
+
24
+ class IsPkeyCheck < AttributeCheck
25
+ MSG = "is not a public key"
26
+
27
+ def call(cco)
28
+ @blockchain = cco.entity.blockchain
29
+ super(cco)
30
+ end
31
+
32
+ def check(attribute, value, errors, options={})
33
+ on = options.fetch(:on, attribute)
34
+ unless value == 0 || anynet_valid_address?(value)
35
+ errors.add(on, options[:message] || MSG)
36
+ end
37
+ end
38
+
39
+ def anynet_valid_address?(addr)
40
+ start_network = Bitcoin.network_name
41
+
42
+ begin
43
+ Bitcoin.network = (/\A1/.match addr) ? :bitcoin : :testnet3
44
+
45
+ return Bitcoin.valid_address?(addr)
46
+ ensure
47
+ Bitcoin.network = start_network
48
+ end
49
+ end
50
+ end
51
+
52
+ class EqualsAttributeCheck < AttributeCheck
53
+ include AgainstAttributeCco
54
+
55
+ MSG = "does not match %s"
56
+
57
+ def check(attribute, value, against, errors, options={})
58
+ errors.add(options.fetch(:on, attribute),
59
+ (options[:message] || MSG) % options[:attribute] ) unless value == against
60
+ end
61
+ end
62
+
63
+ class DoesntEqualAttributeCheck < AttributeCheck
64
+ include AgainstAttributeCco
65
+
66
+ MSG = "matches %s"
67
+
68
+ def check(attribute, value, against, errors, options={})
69
+ errors.add(options.fetch(:on, attribute),
70
+ (options[:message] || MSG) % options[:attribute] ) if value == against
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,115 @@
1
+ #encoding: utf-8
2
+ require_relative 'spec_helper'
3
+ require_relative 'sham/item'
4
+
5
+ describe Dropzone do
6
+ describe "scratch" do
7
+ PRIVATE_KEY_WIF = "92UvdTpmxA6cvD6YeJZSiHW8ff8DsZXL2PHZu9Mg7JY3zbaETJw"
8
+ # Public Address: mi37WkBomHJpUghCn7Vgh3ah33h6L9Nkqw
9
+
10
+ before(:all) do
11
+ Bitcoin.network = :testnet3
12
+ TCPSocket::socks_server = "127.0.0.1"
13
+ TCPSocket::socks_port = 9050
14
+ # Socksify::debug = true
15
+ #RestClient.log = Logger.new STDOUT
16
+
17
+ connection = Dropzone::BitcoinConnection.new :testnet3
18
+ Dropzone::RecordBase.blockchain = connection
19
+ end
20
+
21
+ let(:connection){
22
+ Dropzone::BitcoinConnection.new :testnet3
23
+ }
24
+ let(:genesis_intro) {
25
+ 'In the beginning God created the heavens and the earth. Now the earth was formless and empty, darkness was over the surface of the deep, and the Spirit of God was hovering over the waters. And God said, "Let there be light," and there was light. God saw that the light was good, and he separated the light from the darkness. God called the light "day," and the darkness he called "night." And there was evening, and there was morning-the first day. And God said, "Let there be a vault between the waters to separate water from water." So God made the vault and separated the water under the vault from the water above it. And it was so. God called the vault "sky." And there was evening, and there was morning-the second day. And God said, "Let the water under the sky be gathered to one place, and let dry ground appear." And it was so. God called the dry ground "land," and the gathered waters he called "seas." And God saw that it was good. Then God said, "Let the land produce vegetation: seed-bearing plants and trees on the land that bear fruit with seed in it, according to their various kinds." And it was so. The land produced vegetation: plants bearing seed according to their kinds and trees bearing fruit with seed in it according to their kinds. And God saw that it was good. And there was evening, and there was morning-the third day. And God said, "Let there be lights in the vault of the sky to separate the day from the night, and let them serve as signs to mark sacred times, and days and years, and let them be lights in the vault of the sky to give light on the earth." And it was so. God made two great lights-the greater light to govern the day and the lesser light to govern the night. He also made the stars. God set them in the vault of the sky to give light on the earth, to govern the day and the night, and to separate light from darkness. And God saw that it was good. And there was evening, and there was morning-the fourth day.'}
26
+
27
+ it "Goes through tor" do
28
+ ret = RestClient.get 'http://www.ipchicken.com/'
29
+ parts = /([\d]+\.[\d]+\.[\d]+\.[\d]+)/.match ret
30
+ puts "Running from:"+parts[1].inspect
31
+ end
32
+
33
+ it "issues a spend" do
34
+ # let's parse the keys:
35
+ to = "msj42CCGruhRsFrGATiUuh25dtxYtnpbTx"
36
+ satoshis = 1_000_000 # 0.01 BTC in satoshis
37
+ tip = 500_000 # 0.005 BTC
38
+
39
+ from_key = Bitcoin::Key.from_base58 PRIVATE_KEY_WIF
40
+
41
+ #ret = connection.send_value from_key, to, satoshis, tip
42
+ #puts "Spend Ret: #{ret.inspect}"
43
+ end
44
+
45
+ it "persists a large item" do
46
+ Dropzone::MessageBase.blockchain = connection
47
+
48
+ item = Dropzone::Item.sham!(:build, description: genesis_intro)
49
+ #ret = item.save!(PRIVATE_KEY_WIF)
50
+ #puts ret.inspect
51
+ end
52
+
53
+ it "decodes a large transaction" do
54
+ Dropzone::Item.blockchain = Dropzone::BitcoinConnection.new :testnet3
55
+
56
+ item = Dropzone::Item.find '0b2772eab7823a14fe5d369d3534d4a9d19b19ac0a44855b0aaf9cefcbfe49c7'
57
+
58
+ expect(item.description).to eq(genesis_intro)
59
+ expect(item.price_currency).to eq('BTC')
60
+ expect(item.price_in_units).to eq(100_000_000)
61
+ expect(item.expiration_in).to eq(6)
62
+ expect(item.latitude).to eq(51.500782)
63
+ expect(item.longitude).to eq(-0.124669)
64
+ expect(item.radius).to eq(1000)
65
+ expect(item.receiver_addr).to eq('mfZ1415XX782179875331XX1XXXXXgtzWu')
66
+ end
67
+
68
+ it "enumerates messages by address for a seller" do
69
+ profile = Dropzone::SellerProfile.new 'mi37WkBomHJpUghCn7Vgh3ah33h6L9Nkqw'
70
+
71
+ expect(profile.valid?).to be_truthy
72
+ expect(profile.description).to eq("Test Description")
73
+ expect(profile.alias).to eq("Satoshi")
74
+ expect(profile.communications_pkey).to eq('n3EMs5L3sHcZqRy35cmoPFgw5AzAtWSDUv')
75
+ expect(profile.addr).to eq('mi37WkBomHJpUghCn7Vgh3ah33h6L9Nkqw')
76
+ expect(profile.active?).to be_truthy
77
+ end
78
+
79
+ it "enumerates messages by address for a buyer" do
80
+ profile = Dropzone::BuyerProfile.new 'mqVRfjepJTxxoDgDt892tCybhmjfKCFNyp'
81
+ expect(profile.valid?).to be_truthy
82
+ expect(profile.description).to eq("Test Buyer Description")
83
+ expect(profile.alias).to eq("Test Buyer")
84
+ expect(profile.addr).to eq('mqVRfjepJTxxoDgDt892tCybhmjfKCFNyp')
85
+ expect(profile.active?).to be_truthy
86
+ end
87
+
88
+ it "finds invoices and payments" do
89
+ invoice = Dropzone::Invoice.find '070288a455f14966d77f3877cd21befbd000f23ff3f190e2eb9c1fbbd7d5ac08'
90
+
91
+ expect(invoice.expiration_in).to eq(6000)
92
+ expect(invoice.amount_due).to eq(1000)
93
+ expect(invoice.receiver_addr).to eq('mqVRfjepJTxxoDgDt892tCybhmjfKCFNyp')
94
+ expect(invoice.sender_addr).to eq('mi37WkBomHJpUghCn7Vgh3ah33h6L9Nkqw')
95
+
96
+ payment = Dropzone::Payment.find 'a2598c76593b17c698e4b58637ae4193a27df4c43b8c3b6d3d1e26cc10602325'
97
+
98
+ expect(payment.description).to eq("Fair Exchange")
99
+ expect(payment.invoice_txid).to be_kind_of(String)
100
+ expect(payment.delivery_quality).to eq(8)
101
+ expect(payment.product_quality).to eq(8)
102
+ expect(payment.communications_quality).to eq(4)
103
+ expect(payment.sender_addr).to eq('mqVRfjepJTxxoDgDt892tCybhmjfKCFNyp')
104
+ expect(payment.receiver_addr).to eq('mi37WkBomHJpUghCn7Vgh3ah33h6L9Nkqw')
105
+ end
106
+
107
+ it "Enumerates through listings via Item" do
108
+ # This is diffucult ATM due to blockchain.info not supporting testnet
109
+ # and blockr.io's implementation not including all transactions
110
+ pending
111
+
112
+ end
113
+
114
+ end
115
+ end
@@ -0,0 +1,279 @@
1
+ #encoding: utf-8
2
+ require_relative 'spec_helper'
3
+ require_relative 'sham/buyer'
4
+
5
+ describe Dropzone::BuyerProfile do
6
+ include_context 'globals'
7
+
8
+ describe "accessors" do
9
+ after{ clear_blockchain! }
10
+
11
+ it "compiles a simple profile" do
12
+ Dropzone::Buyer.sham!(:build).save! test_privkey
13
+
14
+ profile = Dropzone::BuyerProfile.new test_pubkey
15
+
16
+ expect(profile.valid?).to be_truthy
17
+ expect(profile.description).to eq("abc")
18
+ expect(profile.alias).to eq("Satoshi")
19
+ expect(profile.addr).to eq(test_pubkey)
20
+ expect(profile.active?).to be_truthy
21
+ end
22
+
23
+ it "combines attributes from mulitple messages" do
24
+ Dropzone::Buyer.sham!(:build).save! test_privkey
25
+ Dropzone::Buyer.sham!(:build, :description => 'xyz').save! test_privkey
26
+
27
+ profile = Dropzone::BuyerProfile.new test_pubkey
28
+
29
+ expect(profile.valid?).to be_truthy
30
+ expect(profile.description).to eq("xyz")
31
+ expect(profile.alias).to eq("Satoshi")
32
+ expect(profile.addr).to eq(test_pubkey)
33
+ expect(profile.active?).to be_truthy
34
+ end
35
+
36
+ it "supports profile transfers" do
37
+ # Standard Buyer:
38
+ Dropzone::Buyer.sham!(:build).save! test_privkey
39
+
40
+ # Buyer Transfer to Tester2:
41
+ Dropzone::Buyer.new( receiver_addr: TESTER2_PUBLIC_KEY,
42
+ transfer_pkey: TESTER2_PUBLIC_KEY).save! test_privkey
43
+
44
+ # And let's update Tester2 for some added complexity:
45
+ Dropzone::Buyer.new( receiver_addr: TESTER2_PUBLIC_KEY,
46
+ :alias => 'New Alias' ).save! TESTER2_PRIVATE_KEY
47
+
48
+ profile = Dropzone::BuyerProfile.new TESTER2_PUBLIC_KEY
49
+
50
+ expect(profile.valid?).to be_truthy
51
+ expect(profile.description).to eq("abc")
52
+ expect(profile.alias).to eq("New Alias")
53
+ expect(profile.addr).to eq(TESTER2_PUBLIC_KEY)
54
+ expect(profile.active?).to be_truthy
55
+ end
56
+
57
+ it "supports a transfer in and transfer out" do
58
+ # Standard Buyer:
59
+ Dropzone::Buyer.sham!(:build).save! test_privkey
60
+
61
+ # Address 1 transfers to Address 2:
62
+ Dropzone::Buyer.new( receiver_addr: TESTER2_PUBLIC_KEY,
63
+ transfer_pkey: TESTER2_PUBLIC_KEY).save! test_privkey
64
+
65
+ # Address 2 transfers to Address 3:
66
+ Dropzone::Buyer.new( receiver_addr: TESTER3_PUBLIC_KEY,
67
+ transfer_pkey: TESTER3_PUBLIC_KEY).save! TESTER2_PRIVATE_KEY
68
+
69
+ profile = Dropzone::BuyerProfile.new TESTER3_PUBLIC_KEY
70
+
71
+ expect(profile.valid?).to be_truthy
72
+ expect(profile.description).to eq("abc")
73
+ expect(profile.alias).to eq("Satoshi")
74
+ expect(profile.addr).to eq(TESTER3_PUBLIC_KEY)
75
+ expect(profile.active?).to be_truthy
76
+ end
77
+
78
+ it "only supports a single transfer in" do
79
+ # Address 1 Declaration:
80
+ Dropzone::Buyer.sham!(:build).save! test_privkey
81
+
82
+ # Address 2 Declaration:
83
+ Dropzone::Buyer.new( description: 'xyz', alias: 'New Alias',
84
+ receiver_addr: TESTER2_PUBLIC_KEY ).save! TESTER2_PRIVATE_KEY
85
+
86
+ # Address 1 transfers to Address 3:
87
+ Dropzone::Buyer.new( receiver_addr: TESTER3_PUBLIC_KEY,
88
+ transfer_pkey: TESTER3_PUBLIC_KEY).save! test_privkey
89
+
90
+ # Address 2 transfers to Address 3:
91
+ Dropzone::Buyer.new( receiver_addr: TESTER3_PUBLIC_KEY,
92
+ transfer_pkey: TESTER3_PUBLIC_KEY).save! TESTER2_PRIVATE_KEY
93
+
94
+ profile = Dropzone::BuyerProfile.new TESTER3_PUBLIC_KEY
95
+
96
+ expect(profile.valid?).to be_truthy
97
+ expect(profile.description).to eq("abc")
98
+ expect(profile.alias).to eq("Satoshi")
99
+ expect(profile.addr).to eq(TESTER3_PUBLIC_KEY)
100
+ expect(profile.transfer_pkey).to be_nil
101
+ expect(profile.active?).to be_truthy
102
+ end
103
+
104
+ it "supports deactivation" do
105
+ # Standard Buyer:
106
+ Dropzone::Buyer.sham!(:build).save! test_privkey
107
+
108
+ # Buyer Deactivates his account:
109
+ Dropzone::Buyer.new( receiver_addr: test_pubkey,
110
+ transfer_pkey: 0).save! test_privkey
111
+
112
+ profile = Dropzone::BuyerProfile.new test_pubkey
113
+
114
+ expect(profile.transfer_pkey).to eq(0)
115
+ expect(profile.active?).to be_falsey
116
+ expect(profile.closed?).to be_truthy
117
+ end
118
+
119
+ it "will stop merging attributes after a transfer out" do
120
+ # Standard Buyer:
121
+ Dropzone::Buyer.sham!(:build).save! test_privkey
122
+
123
+ # Address 1 transfers to Address 2:
124
+ Dropzone::Buyer.new( receiver_addr: TESTER2_PUBLIC_KEY,
125
+ transfer_pkey: TESTER2_PUBLIC_KEY).save! test_privkey
126
+
127
+ # Address 1 changes description:
128
+ Dropzone::Buyer.new( description: 'xyz' ).save! test_privkey
129
+
130
+ profile1 = Dropzone::BuyerProfile.new test_pubkey
131
+ profile2 = Dropzone::BuyerProfile.new TESTER2_PUBLIC_KEY
132
+
133
+ expect(profile1.description).to eq("abc")
134
+ expect(profile1.alias).to eq("Satoshi")
135
+ expect(profile1.addr).to eq(test_pubkey)
136
+ expect(profile1.transfer_pkey).to eq(TESTER2_PUBLIC_KEY)
137
+ expect(profile1.active?).to be_falsey
138
+ expect(profile1.closed?).to be_falsey
139
+
140
+ expect(profile2.description).to eq("abc")
141
+ expect(profile2.alias).to eq("Satoshi")
142
+ expect(profile2.addr).to eq(TESTER2_PUBLIC_KEY)
143
+ expect(profile2.active?).to be_truthy
144
+ expect(profile2.closed?).to be_falsey
145
+ end
146
+
147
+ it "will stop merging attributes after a cancellation" do
148
+ # Standard Buyer:
149
+ Dropzone::Buyer.sham!(:build).save! test_privkey
150
+
151
+ # Address 1 closes its account:
152
+ Dropzone::Buyer.new( receiver_addr: test_pubkey,
153
+ transfer_pkey: 0 ).save! test_privkey
154
+
155
+ # Address 1 changes description:
156
+ Dropzone::Buyer.new( description: 'xyz' ).save! test_privkey
157
+
158
+ profile = Dropzone::BuyerProfile.new test_pubkey
159
+
160
+ expect(profile.valid?).to be_truthy
161
+ expect(profile.description).to eq("abc")
162
+ expect(profile.alias).to eq("Satoshi")
163
+ expect(profile.addr).to eq(test_pubkey)
164
+ expect(profile.transfer_pkey).to eq(0)
165
+ expect(profile.active?).to be_falsey
166
+ end
167
+
168
+ it "will merge attributes in a cancellation message" do
169
+ # Standard Buyer:
170
+ Dropzone::Buyer.sham!(:build).save! test_privkey
171
+
172
+ # Address 1 closes its account:
173
+ Dropzone::Buyer.new( receiver_addr: test_pubkey, description: 'xyz',
174
+ transfer_pkey: 0 ).save! test_privkey
175
+
176
+ profile = Dropzone::BuyerProfile.new test_pubkey
177
+
178
+ expect(profile.valid?).to be_truthy
179
+ expect(profile.description).to eq("xyz")
180
+ expect(profile.alias).to eq("Satoshi")
181
+ expect(profile.addr).to eq(test_pubkey)
182
+ expect(profile.transfer_pkey).to eq(0)
183
+ expect(profile.active?).to be_falsey
184
+ end
185
+
186
+ it "will merge attributes in a transfer message" do
187
+ # Standard Buyer:
188
+ Dropzone::Buyer.sham!(:build).save! test_privkey
189
+
190
+ # Address 1 closes its account:
191
+ Dropzone::Buyer.new( receiver_addr: TESTER2_PUBLIC_KEY, description: 'xyz',
192
+ transfer_pkey: TESTER2_PUBLIC_KEY ).save! test_privkey
193
+
194
+ profile = Dropzone::BuyerProfile.new test_pubkey
195
+
196
+ expect(profile.valid?).to be_truthy
197
+ expect(profile.description).to eq("xyz")
198
+ expect(profile.alias).to eq("Satoshi")
199
+ expect(profile.addr).to eq(test_pubkey)
200
+ expect(profile.transfer_pkey).to eq(TESTER2_PUBLIC_KEY)
201
+ expect(profile.active?).to be_falsey
202
+ end
203
+
204
+ it "won't compile a deactivated transfer" do
205
+ # Standard Buyer:
206
+ Dropzone::Buyer.sham!(:build).save! test_privkey
207
+
208
+ # Address 1 closes its account:
209
+ Dropzone::Buyer.new( receiver_addr: test_pubkey,
210
+ transfer_pkey: 0 ).save! test_privkey
211
+
212
+ # Address 1 transfers its account:
213
+ Dropzone::Buyer.new( receiver_addr: TESTER2_PUBLIC_KEY,
214
+ transfer_pkey: TESTER2_PUBLIC_KEY ).save! test_privkey
215
+
216
+ profile = Dropzone::BuyerProfile.new TESTER2_PUBLIC_KEY
217
+
218
+ expect(profile.valid?).to be_falsey
219
+ end
220
+ end
221
+
222
+ describe "validations" do
223
+ after{ clear_blockchain! }
224
+
225
+ it "requires a valid buyer message" do
226
+ # No messages have been created here yet:
227
+ profile = Dropzone::BuyerProfile.new test_pubkey
228
+
229
+ expect(profile.valid?).to be_falsey
230
+ expect(profile.errors.count).to eq(1)
231
+ expect(profile.errors.on(:addr)).to eq(['profile not found'])
232
+ end
233
+
234
+ it "won't accept a closed account transfer" do
235
+ # Standard Buyer:
236
+ Dropzone::Buyer.sham!(:build).save! test_privkey
237
+
238
+ # Address 1 closes its account:
239
+ Dropzone::Buyer.new( receiver_addr: test_pubkey,
240
+ transfer_pkey: 0 ).save! test_privkey
241
+
242
+ # Address 1 transfers its account:
243
+ Dropzone::Buyer.new( receiver_addr: TESTER2_PUBLIC_KEY,
244
+ transfer_pkey: TESTER2_PUBLIC_KEY ).save! test_privkey
245
+
246
+ profile = Dropzone::BuyerProfile.new TESTER2_PUBLIC_KEY
247
+ expect(profile.valid?).to be_falsey
248
+ expect(profile.errors.count).to eq(1)
249
+ expect(profile.errors.on(:prior_profile)).to eq(['invalid transfer or closed'])
250
+ end
251
+
252
+ it "won't accept a second transfer out" do
253
+ # Standard Buyer:
254
+ Dropzone::Buyer.sham!(:build).save! test_privkey
255
+
256
+ # Address 1 transfers to address 2:
257
+ Dropzone::Buyer.new( receiver_addr: TESTER2_PUBLIC_KEY,
258
+ transfer_pkey: TESTER2_PUBLIC_KEY ).save! test_privkey
259
+
260
+ # Address 1 transfers to address 3:
261
+ Dropzone::Buyer.new( receiver_addr: TESTER3_PUBLIC_KEY,
262
+ transfer_pkey: TESTER3_PUBLIC_KEY ).save! test_privkey
263
+
264
+ profile2 = Dropzone::BuyerProfile.new TESTER2_PUBLIC_KEY
265
+ profile3 = Dropzone::BuyerProfile.new TESTER3_PUBLIC_KEY
266
+
267
+ expect(profile2.valid?).to be_truthy
268
+ expect(profile2.description).to eq("abc")
269
+ expect(profile2.alias).to eq("Satoshi")
270
+ expect(profile2.addr).to eq(TESTER2_PUBLIC_KEY)
271
+ expect(profile2.active?).to be_truthy
272
+
273
+ expect(profile3.valid?).to be_falsey
274
+ expect(profile3.errors.count).to eq(1)
275
+ expect(profile3.errors.on(:prior_profile)).to eq(['invalid transfer or closed'])
276
+ end
277
+
278
+ end
279
+ end