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
@@ -0,0 +1,64 @@
|
|
1
|
+
module Dropzone
|
2
|
+
class Listing < RecordBase
|
3
|
+
include StateAccumulator
|
4
|
+
|
5
|
+
attr_reader :txid, :create_item
|
6
|
+
|
7
|
+
self.message_types = 'ITUPDT'
|
8
|
+
|
9
|
+
state_attr :description, :price_currency, :price_in_units, :expiration_in
|
10
|
+
|
11
|
+
def initialize(txid)
|
12
|
+
@txid = txid
|
13
|
+
|
14
|
+
item = Item.find txid
|
15
|
+
@create_item = item if item && item.valid? && item.message_type == 'ITCRTE'
|
16
|
+
|
17
|
+
if create_item
|
18
|
+
attrs_from create_item
|
19
|
+
|
20
|
+
messages(start_block: create_item.block_height).reverse.each{ |item|
|
21
|
+
attrs_from item if item.create_txid == txid}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def found?
|
26
|
+
!@create_item.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def expiration_at
|
30
|
+
create_item.block_height+expiration_in
|
31
|
+
end
|
32
|
+
|
33
|
+
def addr; from_create :sender_addr; end
|
34
|
+
def latitude; from_create :latitude; end
|
35
|
+
def longitude; from_create :longitude; end
|
36
|
+
def radius; from_create :radius; end
|
37
|
+
|
38
|
+
def seller_profile
|
39
|
+
@seller_profile ||= SellerProfile.new addr if addr
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def from_create(attr)
|
45
|
+
create_item.send attr if create_item
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Listing::Validator < ValidatorBase
|
50
|
+
validate :must_have_active_seller
|
51
|
+
validate :must_have_created_item
|
52
|
+
|
53
|
+
def must_have_active_seller(listing)
|
54
|
+
errors.add :seller_profile, "invalid or missing" if (
|
55
|
+
listing.seller_profile.nil? || !listing.seller_profile.valid? ||
|
56
|
+
!listing.seller_profile.active? )
|
57
|
+
end
|
58
|
+
|
59
|
+
def must_have_created_item(listing)
|
60
|
+
errors.add :create_item, "invalid or missing" unless (
|
61
|
+
listing.create_item && listing.create_item.valid? )
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module Dropzone
|
2
|
+
module MessageValidations
|
3
|
+
def self.included(base)
|
4
|
+
base.validates :receiver_addr, presence: true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module ProfileValidations
|
9
|
+
def self.included(base)
|
10
|
+
base.validates :receiver_addr, equals_attribute: { attribute: :sender_addr },
|
11
|
+
unless: "self.transfer_pkey", if: "self.sender_addr"
|
12
|
+
|
13
|
+
base.validates :transfer_pkey, equals_attribute: { attribute: :receiver_addr,
|
14
|
+
unless: "self.transfer_pkey.nil? || self.transfer_pkey == 0" }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module BillingValidations
|
19
|
+
def self.included(base)
|
20
|
+
base.validates :receiver_addr, doesnt_equal_attribute: { attribute: :sender_addr },
|
21
|
+
if: "self.sender_addr"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class MessageBase < RecordBase
|
26
|
+
DEFAULT_TIP = 20_000
|
27
|
+
|
28
|
+
attr_reader :receiver_addr, :sender_addr, :message_type, :block_height, :txid
|
29
|
+
|
30
|
+
def initialize(attrs = {})
|
31
|
+
data = attrs.delete(:data)
|
32
|
+
|
33
|
+
attrs.merge(data_hash_from_hex(data)).each do |attr, value|
|
34
|
+
instance_variable_set '@%s' % attr, value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def save!(private_key)
|
39
|
+
self.blockchain.save! to_transaction, private_key
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_transaction
|
43
|
+
{receiver_addr: receiver_addr, data: data_to_hex,
|
44
|
+
tip: MessageBase.default_tip }
|
45
|
+
end
|
46
|
+
|
47
|
+
def data_to_hex
|
48
|
+
data_to_hash.inject(message_type.dup) do |ret, (key, value)|
|
49
|
+
value_hex = case
|
50
|
+
when value.nil?
|
51
|
+
nil
|
52
|
+
when self.class.is_attr_int?(key)
|
53
|
+
Bitcoin::Protocol.pack_var_int(value.to_i)
|
54
|
+
when self.class.is_attr_pkey?(key)
|
55
|
+
Bitcoin::Protocol.pack_var_string(
|
56
|
+
(value == 0) ? 0.chr :
|
57
|
+
[anynet_for_address(:hash160_from_address, value)].pack('H*'))
|
58
|
+
else
|
59
|
+
Bitcoin::Protocol.pack_var_string([value.to_s].pack('a*'))
|
60
|
+
end
|
61
|
+
|
62
|
+
(value_hex.nil?) ? ret :
|
63
|
+
ret << Bitcoin::Protocol.pack_var_string(key.to_s) << value_hex
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def data_to_hash
|
68
|
+
self.class.message_attribs.inject({}) do |ret , (short, full)|
|
69
|
+
ret.merge(short => self.send(full))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def anynet_for_address(method, addr)
|
76
|
+
start_network = Bitcoin.network_name
|
77
|
+
|
78
|
+
begin
|
79
|
+
Bitcoin.network = (/\A1/.match addr) ? :bitcoin : :testnet3
|
80
|
+
|
81
|
+
return Bitcoin.send(method, addr)
|
82
|
+
ensure
|
83
|
+
Bitcoin.network = start_network
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def data_hash_from_hex(data)
|
88
|
+
return {} unless /\A(.{6})(.*)/m.match data
|
89
|
+
|
90
|
+
message_type, pairs, data = $1, $2, {}
|
91
|
+
|
92
|
+
while(pairs.length > 0) do
|
93
|
+
short_key, pairs = Bitcoin::Protocol.unpack_var_string(pairs)
|
94
|
+
|
95
|
+
value, pairs = (self.class.is_attr_int? short_key.to_sym) ?
|
96
|
+
Bitcoin::Protocol.unpack_var_int(pairs) :
|
97
|
+
Bitcoin::Protocol.unpack_var_string(pairs)
|
98
|
+
|
99
|
+
if self.class.is_attr_pkey?(short_key.to_sym) && value
|
100
|
+
value = (value == 0.chr) ? 0 :
|
101
|
+
anynet_for_address(:hash160_to_address, value.unpack('H*')[0])
|
102
|
+
end
|
103
|
+
|
104
|
+
full_key = self.class.message_attribs[short_key.to_sym]
|
105
|
+
data[full_key] = value
|
106
|
+
end
|
107
|
+
|
108
|
+
data
|
109
|
+
end
|
110
|
+
|
111
|
+
class << self
|
112
|
+
attr_writer :default_tip
|
113
|
+
|
114
|
+
def default_tip
|
115
|
+
@default_tip || DEFAULT_TIP
|
116
|
+
end
|
117
|
+
|
118
|
+
def message_attribs
|
119
|
+
@message_attribs
|
120
|
+
end
|
121
|
+
|
122
|
+
def is_attr_int?(attr)
|
123
|
+
@message_integers && @message_integers.include?(attr)
|
124
|
+
end
|
125
|
+
|
126
|
+
def is_attr_pkey?(attr)
|
127
|
+
@message_pkeys && @message_pkeys.include?(attr)
|
128
|
+
end
|
129
|
+
|
130
|
+
def message_type(type)
|
131
|
+
@types_include ||= [type]
|
132
|
+
|
133
|
+
define_method(:message_type){ type }
|
134
|
+
end
|
135
|
+
|
136
|
+
def attr_message(attribs)
|
137
|
+
@message_attribs ||= {}
|
138
|
+
@message_attribs.merge! attribs
|
139
|
+
|
140
|
+
attribs.each{ |short_attr, full_attr| attr_reader full_attr }
|
141
|
+
end
|
142
|
+
|
143
|
+
def attr_message_int(attribs)
|
144
|
+
@message_integers ||= []
|
145
|
+
@message_integers += attribs.keys
|
146
|
+
|
147
|
+
attr_message attribs
|
148
|
+
end
|
149
|
+
|
150
|
+
def attr_message_pkey(attribs)
|
151
|
+
@message_pkeys ||= []
|
152
|
+
@message_pkeys += attribs.keys
|
153
|
+
|
154
|
+
attr_message attribs
|
155
|
+
end
|
156
|
+
|
157
|
+
def find(txid)
|
158
|
+
tx = RecordBase.blockchain.tx_by_id txid
|
159
|
+
tx ? self.new(tx) : nil
|
160
|
+
end
|
161
|
+
|
162
|
+
def types_include?(type)
|
163
|
+
@types_include && @types_include.include?(type)
|
164
|
+
end
|
165
|
+
|
166
|
+
def new_message_from(tx)
|
167
|
+
@messages ||= ObjectSpace.each_object(Class).select {|klass|
|
168
|
+
klass < Dropzone::MessageBase }
|
169
|
+
|
170
|
+
if /\A([a-z0-9]{6})/i.match tx[:data]
|
171
|
+
message_klass = @messages.find{|klass| klass.types_include? $1}
|
172
|
+
(message_klass) ? message_klass.new(tx) : nil
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Dropzone
|
2
|
+
class Payment < MessageBase
|
3
|
+
attr_message d: :description, t: :invoice_txid
|
4
|
+
attr_message_int q: :delivery_quality, p: :product_quality,
|
5
|
+
c: :communications_quality
|
6
|
+
|
7
|
+
def invoice
|
8
|
+
@invoice ||= Invoice.find invoice_txid if invoice_txid
|
9
|
+
end
|
10
|
+
|
11
|
+
message_type 'INPAID'
|
12
|
+
end
|
13
|
+
|
14
|
+
class Payment::Validator < ValidatorBase
|
15
|
+
include MessageValidations
|
16
|
+
include BillingValidations
|
17
|
+
|
18
|
+
validates :message_type, format: /\AINPAID\Z/
|
19
|
+
|
20
|
+
validates_if_present :description, is_string: true
|
21
|
+
validates_if_present :invoice_txid, is_string: true
|
22
|
+
|
23
|
+
[:delivery_quality,:product_quality,:communications_quality ].each do |attr|
|
24
|
+
validates_if_present attr, integer: true, inclusion: 0..8
|
25
|
+
end
|
26
|
+
|
27
|
+
validate :must_have_corresponding_invoice
|
28
|
+
|
29
|
+
def must_have_corresponding_invoice(payment)
|
30
|
+
invoice = payment.invoice
|
31
|
+
|
32
|
+
errors.add :invoice_txid, "can't be found" if ( invoice.nil? ||
|
33
|
+
!invoice.valid? || (invoice.sender_addr != payment.receiver_addr) )
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Dropzone
|
2
|
+
class Profile < RecordBase
|
3
|
+
include StateAccumulator
|
4
|
+
|
5
|
+
attr_reader :addr, :transfer_pkey, :prior_profile
|
6
|
+
|
7
|
+
def initialize(addr)
|
8
|
+
@addr = addr
|
9
|
+
|
10
|
+
messages.reverse.each_with_index do |seller, i|
|
11
|
+
# There is a bit of extra logic if the seller profile was transferred
|
12
|
+
# from elsewhere
|
13
|
+
if i == 0 && seller.transfer_pkey
|
14
|
+
# Load the profile from the prior address and pop it off the stack
|
15
|
+
@prior_profile = self.class.new seller.sender_addr
|
16
|
+
|
17
|
+
# It's possible the prior profile was invalid
|
18
|
+
break unless @prior_profile.valid?
|
19
|
+
|
20
|
+
# And it's possible the prior profile was deactivated or not
|
21
|
+
# transferred to us:
|
22
|
+
break unless @prior_profile.transfer_pkey == addr
|
23
|
+
|
24
|
+
attrs_from @prior_profile
|
25
|
+
else
|
26
|
+
# This prevents a second inbound transfer from happening:
|
27
|
+
next if seller.transfer_pkey == addr
|
28
|
+
|
29
|
+
# In case they transferred away :
|
30
|
+
@transfer_pkey = seller.transfer_pkey
|
31
|
+
|
32
|
+
attrs_from seller
|
33
|
+
end
|
34
|
+
|
35
|
+
break if @transfer_pkey
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def closed?; (@transfer_pkey == 0); end
|
40
|
+
def active?; @transfer_pkey.nil? end
|
41
|
+
def found?; messages.length > 0; end
|
42
|
+
end
|
43
|
+
|
44
|
+
module ValidateProfile
|
45
|
+
def self.included(base)
|
46
|
+
base.validate :must_have_declaration
|
47
|
+
base.validate :prior_profile_is_valid
|
48
|
+
base.validate :prior_profile_transferred_to_us
|
49
|
+
end
|
50
|
+
|
51
|
+
def must_have_declaration(profile)
|
52
|
+
errors.add :addr, "profile not found" unless profile.messages.length > 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def prior_profile_is_valid(profile)
|
56
|
+
if profile.prior_profile && !profile.prior_profile.valid?
|
57
|
+
errors.add :prior_profile, "invalid"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def prior_profile_transferred_to_us(profile)
|
62
|
+
if profile.prior_profile && profile.prior_profile.transfer_pkey != profile.addr
|
63
|
+
errors.add :prior_profile, "invalid transfer or closed"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# A profile is different than a Seller message, as it's the concatenation of
|
69
|
+
# Seller messages, and is missing the transfer and sender_addr.
|
70
|
+
class SellerProfile < Profile
|
71
|
+
self.message_types = 'SLUPDT'
|
72
|
+
|
73
|
+
state_attr :description, :alias, :communications_pkey
|
74
|
+
|
75
|
+
class Validator < ValidatorBase; include ValidateProfile; end
|
76
|
+
end
|
77
|
+
|
78
|
+
class BuyerProfile < Profile
|
79
|
+
self.message_types = 'BYUPDT'
|
80
|
+
|
81
|
+
state_attr :description, :alias
|
82
|
+
|
83
|
+
class Validator < ValidatorBase; include ValidateProfile; end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Dropzone
|
2
|
+
class ValidatorBase
|
3
|
+
IS_STRING = /\A.+\Z/
|
4
|
+
|
5
|
+
include Veto.validator
|
6
|
+
|
7
|
+
def self.validates_if_present(attr, options)
|
8
|
+
validates attr, options.merge({unless: "self.%s.nil?" % attr.to_s})
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# This lets us set connection parameters across the entire library.
|
13
|
+
# A cattr_inheritable-esque implementation might be worth adding at some point.
|
14
|
+
class RecordBase
|
15
|
+
def blockchain;
|
16
|
+
RecordBase.blockchain
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?; validator.valid? self; end
|
20
|
+
|
21
|
+
def errors
|
22
|
+
validator.valid? self
|
23
|
+
validator.errors
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def validator; @validator ||= self.class.const_get(:Validator).new; end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
attr_accessor :blockchain
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Dropzone
|
2
|
+
class Seller < MessageBase
|
3
|
+
message_type 'SLUPDT'
|
4
|
+
|
5
|
+
attr_message d: :description, a: :alias
|
6
|
+
attr_message_pkey t: :transfer_pkey, p: :communications_pkey
|
7
|
+
end
|
8
|
+
|
9
|
+
class Seller::Validator < ValidatorBase
|
10
|
+
include MessageValidations
|
11
|
+
include ProfileValidations
|
12
|
+
|
13
|
+
validates :message_type, format: /\ASLUPDT\Z/
|
14
|
+
|
15
|
+
validates_if_present :description, is_string: true
|
16
|
+
validates_if_present :alias, is_string: true
|
17
|
+
|
18
|
+
validates_if_present :communications_pkey, is_pkey: true
|
19
|
+
validates_if_present :transfer_pkey, is_pkey: true
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Dropzone
|
2
|
+
class Session
|
3
|
+
CIPHER_ALGORITHM = 'AES-256-CBC'
|
4
|
+
|
5
|
+
class Unauthenticated < StandardError; end
|
6
|
+
class MissingReceiver < StandardError; end
|
7
|
+
class InvalidWithReceiver < StandardError; end
|
8
|
+
class DerAlreadyExists < StandardError; end
|
9
|
+
class InvalidCommunication < StandardError; end
|
10
|
+
class SessionInvalid < StandardError; end
|
11
|
+
|
12
|
+
attr_accessor :priv_key, :receiver_addr, :session_key, :with, :end_block
|
13
|
+
|
14
|
+
def initialize(priv_key, session_secret, options = {})
|
15
|
+
@priv_key, @session_key = priv_key, OpenSSL::BN.new(session_secret, 16)
|
16
|
+
@end_block = options[:end_block] if options.has_key? :end_block
|
17
|
+
|
18
|
+
# Either you attach to an existing session, or create a new one
|
19
|
+
case
|
20
|
+
when options.has_key?(:receiver_addr)
|
21
|
+
# New Session:
|
22
|
+
@receiver_addr = options[:receiver_addr]
|
23
|
+
when options.has_key?(:with)
|
24
|
+
# Existing Session:
|
25
|
+
raise InvalidWithReceiver unless options[:with].receiver_addr == sender_addr
|
26
|
+
@with = options[:with]
|
27
|
+
@receiver_addr = @with.sender_addr
|
28
|
+
else
|
29
|
+
raise MissingReceiver
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def blockchain; self.class.blockchain; end
|
34
|
+
def sender_addr; blockchain.privkey_to_addr priv_key; end
|
35
|
+
|
36
|
+
# Iv passing is supported only for the purpose of making tests completely
|
37
|
+
# deterministic
|
38
|
+
def send(contents, iv = nil)
|
39
|
+
raise Unauthenticated unless authenticated?
|
40
|
+
|
41
|
+
# Cipher Setup:
|
42
|
+
aes = OpenSSL::Cipher::Cipher.new CIPHER_ALGORITHM
|
43
|
+
aes.encrypt
|
44
|
+
|
45
|
+
iv ||= aes.random_iv
|
46
|
+
aes.iv = iv
|
47
|
+
|
48
|
+
aes.key = symm_key
|
49
|
+
|
50
|
+
# Encrypt Time:
|
51
|
+
cipher = aes.update contents
|
52
|
+
cipher << aes.final
|
53
|
+
|
54
|
+
communicate! contents: cipher.to_s, iv: iv
|
55
|
+
end
|
56
|
+
|
57
|
+
alias :<< :send
|
58
|
+
|
59
|
+
def authenticate!(der = nil)
|
60
|
+
is_init = (communication_init.nil? || authenticated?)
|
61
|
+
|
62
|
+
# If we're already authenticated, we'll try to re-initialize. Presumably
|
63
|
+
# one would want to do this if they lost a secret key, or that key were
|
64
|
+
# somehow compromised
|
65
|
+
if is_init
|
66
|
+
dh = OpenSSL::PKey::DH.new(der || 1024)
|
67
|
+
else
|
68
|
+
raise DerAlreadyExists unless der.nil?
|
69
|
+
dh = OpenSSL::PKey::DH.new with.der
|
70
|
+
end
|
71
|
+
|
72
|
+
dh.priv_key = session_key
|
73
|
+
dh.generate_key!
|
74
|
+
|
75
|
+
communicate! session_pkey: [dh.pub_key.to_s(16)].pack('H*'),
|
76
|
+
der: (is_init) ? dh.public_key.to_der : nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def symm_key
|
80
|
+
return @symm_key if @symm_key
|
81
|
+
|
82
|
+
# If we can't compute, then it's ok to merely indicate this:
|
83
|
+
return nil unless communication_init && communication_auth
|
84
|
+
|
85
|
+
dh = OpenSSL::PKey::DH.new communication_init.der
|
86
|
+
dh.priv_key = session_key
|
87
|
+
dh.generate_key!
|
88
|
+
|
89
|
+
@symm_key = dh.compute_key OpenSSL::BN.new(
|
90
|
+
their_pkey.session_pkey.unpack('H*').first, 16)
|
91
|
+
end
|
92
|
+
|
93
|
+
def authenticated?
|
94
|
+
communication_init && communication_auth
|
95
|
+
end
|
96
|
+
|
97
|
+
def communication_init
|
98
|
+
# NOTE that this returns the newest initialization
|
99
|
+
commun_messages.find(&:is_init?)
|
100
|
+
end
|
101
|
+
|
102
|
+
# This is the response to the init
|
103
|
+
def communication_auth
|
104
|
+
# NOTE that this returns the newest auth, or nil if we encounter an init
|
105
|
+
commun_messages.find{|c|
|
106
|
+
break if c.is_init?
|
107
|
+
c.is_auth? }
|
108
|
+
end
|
109
|
+
|
110
|
+
def their_pkey
|
111
|
+
[communication_init, communication_auth].find{|c|
|
112
|
+
c.sender_addr == receiver_addr && c.receiver_addr == sender_addr }
|
113
|
+
end
|
114
|
+
|
115
|
+
def communications
|
116
|
+
if authenticated?
|
117
|
+
communications = commun_messages(
|
118
|
+
start_block: communication_init.block_height ).reject(&:is_auth?)
|
119
|
+
communications.each{|c| c.symm_key = symm_key}
|
120
|
+
communications
|
121
|
+
else
|
122
|
+
[]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# Addr's of who this conversation is between
|
129
|
+
def between
|
130
|
+
[sender_addr, receiver_addr]
|
131
|
+
end
|
132
|
+
|
133
|
+
def commun_messages(options = {})
|
134
|
+
options[:type] = 'COMMUN'
|
135
|
+
options[:end_block] = @end_block if @end_block
|
136
|
+
options[:between] = between
|
137
|
+
blockchain.messages_by_addr(sender_addr, options)
|
138
|
+
end
|
139
|
+
|
140
|
+
def communicate!(attrs)
|
141
|
+
comm = Communication.new( {receiver_addr: receiver_addr,
|
142
|
+
sender_addr: sender_addr}.merge(attrs) )
|
143
|
+
|
144
|
+
raise InvalidCommunication unless comm.valid?
|
145
|
+
|
146
|
+
comm.save! priv_key
|
147
|
+
end
|
148
|
+
|
149
|
+
class << self
|
150
|
+
attr_writer :blockchain
|
151
|
+
|
152
|
+
def blockchain
|
153
|
+
@blockchain || Dropzone::RecordBase.blockchain
|
154
|
+
end
|
155
|
+
|
156
|
+
def all(addr)
|
157
|
+
blockchain.messages_by_addr(addr, type: 'COMMUN').find_all(&:is_init?)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|