Skip to content

Commit

Permalink
#403 sync_wallets
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor256 committed Aug 10, 2018
1 parent aa6894a commit 6baffea
Show file tree
Hide file tree
Showing 46 changed files with 567 additions and 290 deletions.
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Metrics/AbcSize:
Metrics/BlockLength:
Max: 100
Metrics/ClassLength:
Max: 300
Max: 400
Layout/EndOfLine:
EnforcedStyle: lf
Metrics/ParameterLists:
Expand Down
12 changes: 8 additions & 4 deletions bin/zold
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ require_relative '../lib/zold'
require_relative '../lib/zold/version'
require_relative '../lib/zold/wallet'
require_relative '../lib/zold/wallets'
require_relative '../lib/zold/sync_wallets'
require_relative '../lib/zold/log'
require_relative '../lib/zold/key'
require_relative '../lib/zold/amount'
Expand All @@ -43,6 +44,7 @@ require_relative '../lib/zold/remotes'
require_relative '../lib/zold/upgrades'
require_relative '../lib/zold/version_file'

Thread.current.name = 'main'

Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
Expand Down Expand Up @@ -138,7 +140,9 @@ Available options:"
FileUtils.mkdir_p(opts[:home])
Dir.chdir(opts[:home])

Zold::Upgrades.new(Zold::VersionFile.new('.zoldata/version'), 'upgrades').run
zoldata = File.expand_path(File.join(Dir.pwd, '.zoldata'))

Zold::Upgrades.new(Zold::VersionFile.new(File.join(zoldata, 'version')), 'upgrades').run

# @todo #384:30min This is a workaround, move this code into Upgrades, somehow.
# We should find a way to run these arbitrary scripts and pass arguments
Expand All @@ -150,9 +154,9 @@ Available options:"
require_relative '../upgrades/rename_foreign_wallets'
Zold::RenameForeignWallets.new(Dir.pwd, opts['network'], log).exec

wallets = Zold::Wallets.new('.')
remotes = Zold::Remotes.new(file: './.zoldata/remotes', network: opts['network'])
copies = './.zoldata/copies'
wallets = Zold::SyncWallets.new(Zold::Wallets.new('.'), File.join(zoldata, 'locks'), log: log)
remotes = Zold::Remotes.new(file: File.join(zoldata, 'remotes'), network: opts['network'])
copies = File.join(zoldata, 'copies')

case command
when 'node'
Expand Down
7 changes: 4 additions & 3 deletions fixtures/scripts/distribute-wallet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,16 @@ if [ ! $(echo ${json} | jq -r '.wallets') == "1" ]; then
fi

# Now, we remove the wallet from the second node and expect the first
# one to "spread" it again, almost immediately.
rm ${second}/0000000000000000.z
# one to "spread" it again, almost immediately. The second node should
# have the wallet very soon.
rm -f ${second}/0000000000000000.z
i=0
until zold fetch 0000000000000000 --ignore-score-weakness; do
echo 'Failed to fetch, let us try again'
((i++)) || sleep 0
if ((i==5)); then
echo "The wallet 0000000000000000 has not been spread, after ${i} attempts, here is the log:"
cat ${first}/log.txt
echo "The wallet 0000000000000000 has not been spread, after ${i} attempts"
exit 8
fi
sleep 5
Expand Down
11 changes: 6 additions & 5 deletions lib/zold/commands/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,13 @@ def run(args = [])
private

def create(id, opts)
wallet = @wallets.find(id)
key = Zold::Key.new(file: opts['public-key'])
wallet.init(id, key, network: opts['network'])
@log.info(wallet.id)
@log.debug("Wallet #{Rainbow(wallet).green} created at #{@wallets.path}")
wallet
@wallets.find(id) do |wallet|
wallet.init(id, key, network: opts['network'])
@log.debug("Wallet #{Rainbow(wallet).green} created at #{@wallets.path}")
end
@log.info(id)
id
end
end
end
12 changes: 5 additions & 7 deletions lib/zold/commands/diff.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,23 @@ def run(args = [])
raise 'At least one wallet ID is required' if mine.empty?
stdout = ''
mine.map { |i| Id.new(i) }.each do |id|
stdout += diff(
@wallets.find(id),
Copies.new(File.join(@copies, id)),
opts
)
stdout += diff(id, Copies.new(File.join(@copies, id)), opts)
end
stdout
end

private

def diff(wallet, cps, _)
def diff(id, cps, _)
raise "There are no remote copies, try 'zold fetch' first" if cps.all.empty?
cps = cps.all.sort_by { |c| c[:score] }.reverse
patch = Patch.new(@wallets, log: @log)
cps.each do |c|
patch.join(Wallet.new(c[:path]))
end
before = AtomicFile.new(wallet.path).read
before = @wallets.find(id) do |wallet|
AtomicFile.new(wallet.path).read
end
after = ''
Tempfile.open(['', Wallet::EXTENSION]) do |f|
patch.save(f.path, overwrite: true)
Expand Down
3 changes: 2 additions & 1 deletion lib/zold/commands/fetch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def run(args = [])
private

def fetch(id, cps, opts)
start = Time.now
total = Concurrent::AtomicFixnum.new
nodes = Concurrent::AtomicFixnum.new
done = Concurrent::AtomicFixnum.new
Expand All @@ -87,7 +88,7 @@ def fetch(id, cps, opts)
end
raise "There are no remote nodes, run 'zold remote reset'" if nodes.value.zero?
raise "No nodes out of #{nodes.value} have the wallet #{id}" if done.value.zero? && !opts['quiet-if-absent']
@log.info("#{done.value} copies of #{id} fetched with the total score of \
@log.info("#{done.value} copies of #{id} fetched in #{(Time.now - start).round}s with the total score of \
#{total.value} from #{nodes.value} nodes")
@log.debug("#{cps.all.count} local copies:")
cps.all.each do |c|
Expand Down
7 changes: 4 additions & 3 deletions lib/zold/commands/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ def run(args = [])
mine = Args.new(opts, @log).take || return
raise 'Receiver wallet ID is required' if mine[0].nil?
id = Zold::Id.new(mine[0])
wallet = @wallets.find(id)
raise "Wallet #{id} doesn\'t exist in #{@wallets}, do 'zold pull' first" unless wallet.exists?
invoice(wallet, opts)
@wallets.find(id) do |wallet|
raise "Wallet #{id} doesn\'t exist in #{@wallets}, do 'zold pull' first" unless wallet.exists?
invoice(wallet, opts)
end
end

private
Expand Down
27 changes: 14 additions & 13 deletions lib/zold/commands/merge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,22 @@ def merge(id, cps, opts)
merge_one(opts, patch, wallet, "#{c[:name]}/#{idx}/#{c[:score]}")
score += c[:score]
end
wallet = @wallets.find(id)
if wallet.exists?
merge_one(opts, patch, wallet, 'localhost')
@log.debug("Local copy of #{id} merged: #{patch}")
else
@log.debug("Local copy of #{id} is absent, nothing to merge")
end
modified = patch.save(wallet.path, overwrite: true)
if modified
@log.info("#{cps.count} copies with the total score of #{score} successfully merged \
@wallets.find(id) do |wallet|
if wallet.exists?
merge_one(opts, patch, wallet, 'localhost')
@log.debug("Local copy of #{id} merged: #{patch}")
else
@log.debug("Local copy of #{id} is absent, nothing to merge")
end
modified = patch.save(wallet.path, overwrite: true)
if modified
@log.info("#{cps.count} copies with the total score of #{score} successfully merged \
into #{wallet.id}/#{wallet.balance}/#{wallet.txns.count}t")
else
@log.info("Nothing changed in #{wallet.id} after merge of #{cps.count} copies")
else
@log.info("Nothing changed in #{wallet.id} after merge of #{cps.count} copies")
end
modified
end
modified
end

def merge_one(opts, patch, wallet, name)
Expand Down
34 changes: 22 additions & 12 deletions lib/zold/commands/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
require_relative '../metronome'
require_relative '../wallet'
require_relative '../wallets'
require_relative '../hungry_wallets'
require_relative '../remotes'
require_relative '../verbose_thread'
require_relative '../node/entrance'
Expand All @@ -53,10 +52,10 @@ module Zold
# NODE command
class Node
def initialize(wallets:, remotes:, copies:, log: Log::Quiet.new)
@wallets = HungryWallets.new(wallets)
@remotes = remotes
@copies = copies
@log = log
@wallets = wallets
end

def run(args = [])
Expand Down Expand Up @@ -164,6 +163,9 @@ def run(args = [])
address = "#{host}:#{port}".downcase
@log.info("Node location: #{address}")
@log.info("Local address: http://localhost:#{opts['bind-port']}/")
@log.info("Remote nodes (#{@remotes.all.count}): \
#{@remotes.all.map { |r| "#{r[:host]}:#{r[:port]}" }.join(', ')}")
@log.info("Wallets at: #{@wallets.path}")
Front.set(
:server_settings,
Logger: WebrickLog.new(@log),
Expand Down Expand Up @@ -193,14 +195,16 @@ def run(args = [])
Front.set(:node_alias, node_alias)
invoice = opts[:invoice]
unless invoice.include?('@')
if @wallets.find(Id.new(invoice)).exists?
@log.info("Wallet #{invoice} already exists locally, won't pull")
else
@log.info("The wallet #{invoice} is not available locally, will pull now...")
require_relative 'pull'
Pull.new(wallets: @wallets, remotes: @remotes, copies: @copies, log: @log).run(
['pull', invoice, "--network=#{opts['network']}"]
)
@wallets.find(Id.new(invoice)) do |wallet|
if wallet.exists?
@log.info("Wallet #{invoice} already exists locally, won't pull")
else
@log.info("The wallet #{invoice} is not available locally, will pull now...")
require_relative 'pull'
Pull.new(wallets: @wallets, remotes: @remotes, copies: @copies, log: @log).run(
['pull', invoice, "--network=#{opts['network']}"]
)
end
end
require_relative 'invoice'
invoice = Invoice.new(wallets: @wallets, log: @log).run(['invoice', invoice])
Expand All @@ -210,7 +214,13 @@ def run(args = [])
AsyncEntrance.new(
SpreadEntrance.new(
SyncEntrance.new(
Entrance.new(@wallets, @remotes, @copies, address, log: @log, network: opts['network'])
Entrance.new(
@wallets,
@remotes, @copies, address,
log: @log, network: opts['network']
),
File.join(Dir.pwd, '.zoldata/entrance'),
log: @log
),
@wallets, @remotes, address,
log: @log,
Expand Down Expand Up @@ -306,7 +316,7 @@ def metronome(farm, opts)
metronome.add(Routines::Spread.new(opts, @wallets, @remotes, log: @log))
unless opts['standalone']
require_relative 'routines/reconnect'
metronome.add(Routines::Reconnect.new(opts, @remotes, farm, network: opts['network'], log: Log::Quiet.new))
metronome.add(Routines::Reconnect.new(opts, @remotes, farm, network: opts['network'], log: @log))
end
@log.info('Metronome created')
metronome
Expand Down
25 changes: 16 additions & 9 deletions lib/zold/commands/pay.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ def run(args = [])
mine = Args.new(opts, @log).take || return
raise 'Payer wallet ID is required as the first argument' if mine[0].nil?
id = Id.new(mine[0])
from = @wallets.find(id)
raise "Wallet #{id} doesn't exist, do 'zold pull' first" unless from.exists?
raise 'Recepient\'s invoice or wallet ID is required as the second argument' if mine[1].nil?
invoice = mine[1]
unless invoice.include?('@')
Expand All @@ -78,20 +76,29 @@ def run(args = [])
raise 'Amount is required (in ZLD) as the third argument' if mine[2].nil?
amount = Amount.new(zld: mine[2].to_f)
details = mine[3] || '-'
if Tax.new(from).in_debt? && !opts['dont-pay-taxes']
require_relative 'taxes'
Taxes.new(wallets: @wallets, remotes: @remotes, log: @log).run(
['taxes', 'pay', "--private-key=#{opts['private-key']}", id.to_s]
)
taxes(id)
@wallets.find(id) do |from|
pay(from, invoice, amount, details, opts)
end
pay(from, invoice, amount, details, opts)
return if opts['skip-propagate']
require_relative 'propagate'
Propagate.new(wallets: @wallets, log: @log).run(['propagate', from.id.to_s])
Propagate.new(wallets: @wallets, log: @log).run(['propagate', id.to_s])
end

private

def taxes(id)
debt = @wallets.find(id) do |wallet|
raise "Wallet #{id} doesn't exist, do 'zold pull' first" unless wallet.exists?
Tax.new(wallet).in_debt? && !opts['dont-pay-taxes']
end
return unless debt
require_relative 'taxes'
Taxes.new(wallets: @wallets, remotes: @remotes, log: @log).run(
['taxes', 'pay', "--private-key=#{opts['private-key']}", id.to_s]
)
end

def pay(from, invoice, amount, details, opts)
unless opts.force?
raise 'The amount can\'t be zero' if amount.zero?
Expand Down
45 changes: 24 additions & 21 deletions lib/zold/commands/propagate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,38 +51,41 @@ def run(args = [])
mine = @wallets.all if mine.empty?
modified = []
mine.map { |i| Id.new(i) }.each do |id|
modified += propagate(@wallets.find(id), opts)
modified += propagate(id, opts)
end
modified
end

private

# Returns list of Wallet IDs which were affected
def propagate(wallet, _)
me = wallet.id
def propagate(id, _)
modified = []
wallet.txns.select { |t| t.amount.negative? }.each do |t|
target = @wallets.find(t.bnf)
unless target.exists?
@log.debug("#{t.amount * -1} to #{t.bnf}: wallet is absent")
next
@wallets.find(id) do |wallet|
wallet.txns.select { |t| t.amount.negative? }.each do |t|
next if t.bnf == id
@wallets.find(t.bnf) do |target|
unless target.exists?
@log.debug("#{t.amount * -1} to #{t.bnf}: wallet is absent")
next
end
unless target.network == wallet.network
@log.error("#{t.amount * -1} to #{t.bnf}: network mismatch, '#{target.network}'!='#{wallet.network}'")
next
end
next if target.has?(t.id, id)
unless target.prefix?(t.prefix)
@log.error("#{t.amount * -1} to #{t.bnf}: wrong prefix")
next
end
target.add(t.inverse(id))
@log.info("#{t.amount * -1} arrived to #{t.bnf}: #{t.details}")
modified << t.bnf
end
end
unless target.network == wallet.network
@log.error("#{t.amount * -1} to #{t.bnf}: network mismatch, '#{target.network}'!='#{wallet.network}'")
next
end
next if target.has?(t.id, me)
unless target.prefix?(t.prefix)
@log.error("#{t.amount * -1} to #{t.bnf}: wrong prefix")
next
end
target.add(t.inverse(me))
@log.info("#{t.amount * -1} arrived to #{t.bnf}: #{t.details}")
modified << t.bnf
end
modified.uniq!
@log.debug("Wallet #{me} propagated successfully, #{modified.count} wallets affected")
@log.debug("Wallet #{id} propagated successfully, #{modified.count} wallets affected")
modified
end
end
Expand Down
Loading

0 comments on commit 6baffea

Please sign in to comment.