Class: DropZoneCommand

Inherits:
Object
  • Object
show all
Defined in:
lib/dropzone/command.rb

Overview

This is mostly intended for use by the CLI client, though it’s conceivable that others may find it useful in some contexts.

Constant Summary collapse

MAX_TABLE_WIDTH =
80
LISTING_ATTRS =
[ :latitude, :longitude, :radius, :price_currency, 
:price_in_units, :description, :expiration_in ]
PROFILE_ATTRS =
[ :alias, :description ]
INVOICE_ATTRS =
[ :amount_due, :expiration_in ]
PAYMENT_ATTRS =
[:description, :delivery_quality, :product_quality, 
:communications_quality, :invoice_txid ]
ADDRESS_TO_SELF =
lambda{|privkey, args, params|
params.merge!(receiver_addr: privkey.addr) }
RECORD_BY_FIND =
lambda{|klass, id| klass.find id }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(is_spec = false) ⇒ DropZoneCommand

Returns a new instance of DropZoneCommand.



216
217
218
219
# File 'lib/dropzone/command.rb', line 216

def initialize(is_spec = false)
  @is_spec = is_spec
  network! Bitcoin.network_name
end

Instance Attribute Details

#connectionObject (readonly)

Returns the value of attribute connection.



221
222
223
# File 'lib/dropzone/command.rb', line 221

def connection
  @connection
end

Class Method Details

.create_command(action, klass, label, *attributes, &block) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/dropzone/command.rb', line 101

def create_command(action, klass, label, *attributes, &block)
  define_method(action) do |args, options|
    privkey = privkey_from args

    params = parameterize options, *attributes

    block.call privkey, args, params if block_given?

    message = klass.new params

    if (message.valid?)
      txid = message.save! privkey.to_base58

      puts_object '%s: %s' % [label, message.receiver_addr], 
        'Tx: %s' % txid, attributes, message
    else
      puts "Errors Found in %s:" % label
      message.errors.each_pair do |attr, reasons|
        puts ' * "%s": %s' % [attr, reasons.join(', ')]
      end
    end
  end
end

.local_persistenceObject



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
# File 'lib/dropzone/command.rb', line 71

def local_persistence
  unless @local_persistence
    config_dir = File.join(Dir.home, ".dropzone")
    Dir.mkdir config_dir, 0700 unless Dir.exists? config_dir

    @local_persistence = Sequel.connect('sqlite://%s/dropzone.db' % config_dir )
  end

  @local_persistence.create_table :communication_keys do
    primary_key :id
    String :sender_addr
    String :receiver_addr
    String :secret
  end unless @local_persistence.table_exists? :communication_keys

  @local_persistence.create_table :chats do
    primary_key :id
    String :session_txid
    Integer :last_read_message_count
  end unless @local_persistence.table_exists? :chats

  @local_persistence.create_table :addresses do
    primary_key :id
    String :addr
    String :label
  end unless @local_persistence.table_exists? :addresses

  @local_persistence
end

.local_persistence=(store) ⇒ Object



67
68
69
# File 'lib/dropzone/command.rb', line 67

def local_persistence=(store)
  @local_persistence = store
end

.show_command(action, klass, label, finder, *attributes, &block) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/dropzone/command.rb', line 125

def show_command(action, klass, label, finder, *attributes, &block)
  define_method(action) do |args, options|
    id = self.send finder,  args
    
    record = (block_given?) ? block.call(klass, id) : klass.new(id)

    if record.respond_to?(:found?) and !record.found?
      puts "%s Not Found" % label
    else
      params = attributes.collect{|attr| [attr,record.send(attr)]}.to_h

      puts_object '%s: %s' % [label, id], nil, attributes, record
    end
  end
end

Instance Method Details

#balance(args, options) ⇒ Object

Raises:

  • (OptionParser::MissingArgument)


406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/dropzone/command.rb', line 406

def balance(args, options)
  addr = args.first

  raise OptionParser::MissingArgument, "addr" unless addr
   
  network! (/\A1/.match addr) ? :bitcoin : :testnet3

  raise OptionParser::InvalidArgument, "addr" unless Bitcoin.valid_address? addr

  balance = connection.bitcoin.getbalance addr

  puts_table '%s: %s' % ['Address', addr], nil, [
    ['Balance', balance ] ]
end

#chat_list(args, options) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/dropzone/command.rb', line 251

def chat_list(args, options)
  network! :testnet3

  privkey = privkey_from args, :testnet3

  Dropzone::Session.all(privkey.addr).each do |init|
    session = session_for privkey, init

    chat_with = [init.sender_addr, init.receiver_addr].find{|a| a != privkey.addr}

    chats_cache = local_persistence[:chats].first(session_txid: init.txid)
    read_messages = (chats_cache) ? chats_cache[:last_read_message_count] : 0
    total_messages = session.communications.length
    unread_messages = (total_messages-read_messages)

    puts_table '%s: %s' % ['Session', init.txid], nil, [ 
      ['Address', chat_with], 
      ['Messages', '%d Unread / %d Total' % [unread_messages, total_messages ] ] ]
  end
end

#chat_new(args, options) ⇒ Object

Raises:

  • (OptionParser::MissingArgument)


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/dropzone/command.rb', line 231

def chat_new(args, options)
  network! :testnet3

  privkey = privkey_from args, :testnet3

  receiver_addr = args[1]  if args.length > 1

  raise OptionParser::MissingArgument, 'addr' unless (
    receiver_addr && Bitcoin.valid_address?(receiver_addr) )

  session = Dropzone::Session.new privkey.to_base58,
    secret_for(privkey.addr, receiver_addr),
    receiver_addr: receiver_addr 

  txid = session.authenticate!

  puts_table '%s: %s' % ['Session', txid], nil, [ 
    [:sender_addr, privkey.addr], [:receiver_addr, receiver_addr] ]
end

#chat_say(args, options) ⇒ Object

Raises:

  • (OptionParser::MissingArgument)


272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/dropzone/command.rb', line 272

def chat_say(args, options)
  network! :testnet3

  privkey = privkey_from args, :testnet3

  txid = args[1]  if args.length > 1
  message = args[2]  if args.length > 2

  raise OptionParser::MissingArgument, 'txid' unless txid
  raise OptionParser::MissingArgument, 'message' unless message

  comm_init = Dropzone::Communication.find txid

  raise "Invalid Session" unless comm_init && comm_init.is_init?

  session = session_for privkey, comm_init

  if !session.authenticated?
    if comm_init.sender_addr == privkey.addr
      raise "The receiver has not yet authenticated your request"
    else
      session.authenticate!

      # This allows us to re-query the the session communications, and 
      # retrive the auth_message we just created.
      # NOTE: We nonetheless fail (sometimes) with a 
      #  Dropzone::Session::Unauthenticated message as it takes a bit of time to 
      #  populate the authentication relay into the mempool:
      if Dropzone::BitcoinConnection.cache
        Dropzone::BitcoinConnection.cache.invalidate_listtransactions!

        puts "Waiting 10 seconds for authorization to propagate to the mempool..."
        sleep 10
      end
    end
  end

  comm_txid = session << message

  puts_table '%s: %s' % ['Chat', comm_txid], nil, [ 
    ['Session', txid], 
    [:sender_addr, privkey.addr], 
    [:message, message] ]
end

#chat_show(args, options) ⇒ Object

Raises:

  • (OptionParser::MissingArgument)


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/dropzone/command.rb', line 317

def chat_show(args, options)
  network! :testnet3

  privkey = privkey_from args, :testnet3

  txid = args[1]  if args.length > 1

  raise OptionParser::MissingArgument, 'txid' unless txid

  comm_init = Dropzone::Communication.find txid

  raise "Invalid Session" unless comm_init && comm_init.is_init?

  session = session_for privkey, comm_init

  update_attrs = {last_read_message_count: session.communications.length}

  cache = local_persistence[:chats]
  unless 1 == cache.where(session_txid: txid).update(update_attrs)
    cache.insert update_attrs.merge(session_txid: txid)
  end

  puts_table '%s: %s' % ['Chat', txid], nil,
   session.communications.reverse.collect{|comm|
    [comm.sender_addr, comm.contents_plain] }

  # TODO: Determine how many messages to show here, instead of 'all'
end

#listing_find(args, options) ⇒ Object

Raises:

  • (OptionParser::MissingArgument)


346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/dropzone/command.rb', line 346

def listing_find(args, options)
  block_depth = args[0]  if args.length > 0

  raise OptionParser::MissingArgument, "block_depth" unless block_depth

  block_depth = block_depth.to_i

  raise OptionParser::InvalidArgument, "block_depth" unless block_depth >= 0

  location_attrs = [:latitude, :longitude, :radius]

  params = parameterize options, *location_attrs+[:start_at]

  via_location = location_attrs.collect{|k| params[k]}

  raise "missing one or more of: latitude, longitude, or radius."  if (
    via_location.any? && !via_location.all? )

  start_at = params[:start_at] ? params[:start_at].to_i : connection.block_height

  finder_method = (via_location.all?) ? 
    [ :find_in_radius, start_at, block_depth, *via_location] :
    [ :find_creates_since_block, start_at, block_depth] 

  Dropzone::Item.send(*finder_method) do |item|
    puts_object '%s: %s' % ['Listing', item.txid], nil, LISTING_ATTRS, item
  end
end

#network!(network_name) ⇒ Object



223
224
225
226
227
228
229
# File 'lib/dropzone/command.rb', line 223

def network!(network_name)
  unless @is_spec
    Bitcoin.network = network_name
    @connection = Dropzone::BitcoinConnection.new network_name
    Dropzone::RecordBase.blockchain = @connection
  end
end

#send_value(args, options) ⇒ Object

Raises:

  • (OptionParser::MissingArgument)


375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/dropzone/command.rb', line 375

def send_value(args, options)
  dest_addr = args[1]  if args.length > 1

  raise OptionParser::MissingArgument, "dest_addr" unless dest_addr

  network! (/\A1/.match dest_addr) ? :bitcoin : :testnet3

  raise OptionParser::InvalidArgument, "dest_addr" unless Bitcoin.valid_address? dest_addr

  amnt_btc = args[2] if args.length > 2

  raise OptionParser::MissingArgument, "amnt_btc" unless amnt_btc

  amnt_btc = BigDecimal.new amnt_btc

  raise OptionParser::InvalidArgument, "amnt_btc" unless amnt_btc > 0

  amnt_satoshis = (amnt_btc * Dropzone::BitcoinConnection::SATOSHIS_IN_BTC).to_i

  privkey = privkey_from args, Bitcoin.network_name

  txid = connection.send_value privkey, dest_addr, amnt_satoshis, 
    Dropzone::MessageBase.default_tip

  puts_table '%s: %s' % ['Transaction', txid], nil, [
    ['From', privkey.addr ] ,
    ['To', dest_addr ] ,
    ['Amount (BTC)', amnt_btc.to_s('F').to_s ] 
  ]
end