class Zold::Wallet

A single wallet

Constants

EXT

The extension of the wallet files

MAINET

The name of the main production network. All other networks must have different names.

Public Class Methods

new(file) click to toggle source

The constructor of the wallet, from the file. The file may be absent at the time of creating the object. Later, don't forget to call init() in order to initialize the wallet, if it's absent.

# File lib/zold/wallet.rb, line 60
def initialize(file)
  unless file.end_with?(Wallet::EXT, Copies::EXT)
    raise "Wallet file must end with #{Wallet::EXT} or #{Copies::EXT}: #{file}"
  end
  @file = File.absolute_path(file)
  @txns = CachedTxns.new(Txns.new(@file))
  @head = CachedHead.new(Head.new(@file))
end

Public Instance Methods

==(other) click to toggle source
# File lib/zold/wallet.rb, line 69
def ==(other)
  to_s == other.to_s
end
add(txn) click to toggle source

Add a transaction to the wallet.

# File lib/zold/wallet.rb, line 160
def add(txn)
  raise 'The txn has to be of type Txn' unless txn.is_a?(Txn)
  raise "Wallet #{id} can't pay itself: #{txn}" if txn.bnf == id
  raise "The amount can't be zero in #{id}: #{txn}" if txn.amount.zero?
  if txn.amount.negative? && includes_negative?(txn.id)
    raise "Negative transaction with the same ID #{txn.id} already exists in #{id}"
  end
  if txn.amount.positive? && includes_positive?(txn.id, txn.bnf)
    raise "Positive transaction with the same ID #{txn.id} and BNF #{txn.bnf} already exists in #{id}"
  end
  raise "The tax payment already exists in #{id}: #{txn}" if Tax.new(self).exists?(txn.details)
  File.open(path, 'a') { |f| f.print "#{txn}\n" }
  @txns.flush
end
age() click to toggle source

Age of wallet in hours.

# File lib/zold/wallet.rb, line 212
def age
  list = txns
  list.empty? ? 0 : (Time.now - list.min_by(&:date).date) / (60 * 60)
end
balance() click to toggle source

Returns current wallet balance.

# File lib/zold/wallet.rb, line 133
def balance
  txns.inject(Amount::ZERO) { |sum, t| sum + t.amount }
end
digest() click to toggle source

Returns a pseudo-unique hexadecimal digest of the wallet content.

# File lib/zold/wallet.rb, line 207
def digest
  OpenSSL::Digest::SHA256.file(path).hexdigest
end
exists?() click to toggle source

Returns TRUE if the wallet file exists.

# File lib/zold/wallet.rb, line 103
def exists?
  File.exist?(path)
end
flush() click to toggle source

Flush the in-memory cache and force the object to load all data from the disc again.

# File lib/zold/wallet.rb, line 240
def flush
  @head.flush
  @txns.flush
end
id() click to toggle source

Returns the wallet ID.

# File lib/zold/wallet.rb, line 128
def id
  Id.new(@head.fetch[2])
end
includes_negative?(id, bnf = nil) click to toggle source

Returns TRUE if the wallet contains a payment sent with the specified ID, which was sent to the specified beneficiary.

# File lib/zold/wallet.rb, line 177
def includes_negative?(id, bnf = nil)
  raise 'The txn ID has to be of type Integer' unless id.is_a?(Integer)
  !txns.find { |t| t.id == id && (bnf.nil? || t.bnf == bnf) && t.amount.negative? }.nil?
end
includes_positive?(id, bnf) click to toggle source

Returns TRUE if the wallet contains a payment received with the specified ID, which was sent by the specified beneficiary.

# File lib/zold/wallet.rb, line 184
def includes_positive?(id, bnf)
  raise 'The txn ID has to be of type Integer' unless id.is_a?(Integer)
  raise 'The bnf has to be of type Id' unless bnf.is_a?(Id)
  !txns.find { |t| t.id == id && t.bnf == bnf && !t.amount.negative? }.nil?
end
init(id, pubkey, overwrite: false, network: 'test') click to toggle source

Creates an empty wallet with the specified ID and public key.

# File lib/zold/wallet.rb, line 113
def init(id, pubkey, overwrite: false, network: 'test')
  raise "File '#{path}' already exists" if File.exist?(path) && !overwrite
  raise "Invalid network name '#{network}'" unless network =~ /^[a-z]{4,16}$/
  FileUtils.mkdir_p(File.dirname(path))
  IO.write(path, "#{network}\n#{PROTOCOL}\n#{id}\n#{pubkey.to_pub}\n\n")
  @txns.flush
  @head.flush
end
key() click to toggle source

Returns the public key of the wallet.

# File lib/zold/wallet.rb, line 197
def key
  Key.new(text: @head.fetch[3])
end
mnemo() click to toggle source

Returns a convenient printable mnemo code of the wallet (mostly useful for logs).

# File lib/zold/wallet.rb, line 79
def mnemo
  "#{id}/#{balance.to_zld(4)}/#{txns.count}t/#{digest[0, 6]}/#{Size.new(size)}"
end
mtime() click to toggle source

Returns the time of when the wallet file was recently modified.

# File lib/zold/wallet.rb, line 202
def mtime
  File.mtime(path)
end
network() click to toggle source

Returns the network ID of the wallet.

# File lib/zold/wallet.rb, line 89
def network
  n = @head.fetch[0]
  raise "Invalid network name '#{n}'" unless n =~ /^[a-z]{4,16}$/
  n
end
path() click to toggle source

Returns the absolute path of the wallet file (it may be absent).

# File lib/zold/wallet.rb, line 108
def path
  @file
end
prefix?(prefix) click to toggle source

Returns TRUE if the public key of the wallet includes this payment prefix of the invoice.

# File lib/zold/wallet.rb, line 192
def prefix?(prefix)
  key.to_pub.include?(prefix)
end
protocol() click to toggle source

Returns the protocol ID of the wallet file.

# File lib/zold/wallet.rb, line 96
def protocol
  v = @head.fetch[1]
  raise "Invalid protocol version name '#{v}'" unless v =~ /^[0-9]+$/
  v.to_i
end
refurbish() click to toggle source

Resaves the content of the wallet to the disc in the right format. All unnecessary space and EOL-s are removed. This operation is required in order to make sure two wallets with the same content are identical, no matter whether they were formatted differently.

# File lib/zold/wallet.rb, line 233
def refurbish
  IO.write(path, (@head.fetch + [''] + @txns.fetch.map(&:to_s)).join("\n") + "\n")
  @txns.flush
end
root?() click to toggle source

Returns TRUE if it's a root wallet.

# File lib/zold/wallet.rb, line 123
def root?
  id == Id::ROOT
end
size() click to toggle source

Size of the wallet file in bytes. If the file doesn't exist an exception will be raised.

# File lib/zold/wallet.rb, line 219
def size
  raise "The wallet file #{path} doesn't exist" unless File.exist?(path)
  File.size(path)
end
sub(amount, invoice, pvt, details = '-', time: Time.now) click to toggle source

Add a payment transaction to the wallet.

# File lib/zold/wallet.rb, line 138
def sub(amount, invoice, pvt, details = '-', time: Time.now)
  raise 'The amount has to be of type Amount' unless amount.is_a?(Amount)
  raise "The amount can't be negative: #{amount}" if amount.negative?
  raise 'The pvt has to be of type Key' unless pvt.is_a?(Key)
  prefix, target = invoice.split('@')
  tid = max + 1
  raise 'Too many transactions already, can\'t add more' if max > 0xffff
  txn = Txn.new(
    tid,
    time,
    amount * -1,
    prefix,
    Id.new(target),
    details
  )
  txn = txn.signed(pvt, id)
  raise "Invalid private key for the wallet #{id}" unless Signature.new(network).valid?(key, id, txn)
  add(txn)
  txn
end
to_s() click to toggle source
# File lib/zold/wallet.rb, line 73
def to_s
  id.to_s
end
to_text() click to toggle source

Convert the content of the wallet to the text.

# File lib/zold/wallet.rb, line 84
def to_text
  (@head.fetch + [''] + @txns.fetch.map(&:to_text)).join("\n")
end
txns() click to toggle source

Retrieve the total list of all transactions.

# File lib/zold/wallet.rb, line 225
def txns
  @txns.fetch
end

Private Instance Methods

max() click to toggle source

Calculate the maximum transaction ID visible currently in the wallet. We go through them all and find the largest number. If there are no transactions, zero is returned.

# File lib/zold/wallet.rb, line 250
def max
  negative = txns.select { |t| t.amount.negative? }
  negative.empty? ? 0 : negative.max_by(&:id).id
end