#!/usr/bin/env ruby # frozen_string_literal: true # Copyright (c) 2018 Yegor Bugayenko # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the 'Software'), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. STDOUT.sync = true require 'slop' require 'rainbow' require 'concurrent' require 'zold/log' require 'zold/age' require 'zold/wallet' require 'zold/wallets' require 'zold/sync_wallets' require 'zold/cached_wallets' require 'zold/remotes' require 'zold/commands/list' require_relative '../lib/zold/stress/round' require_relative '../lib/zold/stress/stats' require_relative '../lib/zold/stress/summary' require_relative '../lib/zold/stress/air' Thread.current.name = 'main' Encoding.default_external = Encoding::UTF_8 Encoding.default_internal = Encoding::UTF_8 log = Zold::Log::REGULAR vlog = Zold::Log::ERRORS begin opts = Slop.parse(ARGV, strict: false, suppress_errors: true) do |o| o.banner = "Usage: zold-stress [options] Available options:" o.integer '-r', '--rounds', 'Total amount of paying rounds to complete (default: 16)', default: 16 o.integer '-w', '--wait', 'For how to long to wait for all payments to arrive (default: 600 seconds)', default: 600 o.integer '-p', '--pool', 'From how many wallets to send payments (default: 8)', default: 8 o.integer '-t', '--threads', "How many threads to use for each operation (default: #{Concurrent.processor_count * 8})", default: Concurrent.processor_count * 8 o.integer '-b', '--batch', 'How many transactions to send in each round (default: 64)', default: 64 o.string '--private-key', 'The location of RSA private key (default: ~/.ssh/id_rsa)', require: true, default: File.expand_path('~/.ssh/id_rsa') o.string '--home', "Home directory (default: #{Dir.pwd})", default: Dir.pwd o.string '--network', "The name of the network we work in (default: #{Zold::Wallet::MAINET}", required: true, default: Zold::Wallet::MAINET o.bool '-h', '--help', 'Show these instructions' o.on '--verbose', 'Enable extra logging information' do log = Zold::Log::VERBOSE vlog = Zold::Log::REGULAR end o.on '--no-colors', 'Disable colors in the ouput' do Rainbow.enabled = false end end if opts.help? log.info(opts.to_s) exit end home = File.expand_path(opts[:home]) FileUtils.mkdir_p(home) Dir.chdir(home) zoldata = File.join(home, '.zoldata') wallets = Zold::SyncWallets.new( Zold::CachedWallets.new(Zold::Wallets.new(home)), log: log ) remotes = Zold::Remotes.new(file: File.join(zoldata, 'remotes'), network: opts['network']) if remotes.all.empty? remotes.defaults log.info("The list of remotes has got default nodes, there are #{remotes.all.count} total") end copies = File.join(zoldata, 'copies') stats = Zold::Stress::Stats.new(log: vlog) summary = Zold::Stress::Summary.new(stats, opts['batch']) air = Zold::Stress::Air.new round = Zold::Stress::Round.new( pvt: Zold::Key.new(file: opts['private-key']), wallets: wallets, remotes: remotes, copies: copies, stats: stats, air: air, log: log, vlog: vlog, opts: opts ) log.info("Time: #{Time.now.utc.iso8601}; CPUs: #{Concurrent.processor_count}") log.info("Home directory: #{home}") log.info("Ruby version: #{RUBY_VERSION}/#{RUBY_PLATFORM}") log.info("Zold gem version: #{Zold::VERSION}") log.info("Zold protocol version: #{Zold::PROTOCOL}") log.info("Network ID: #{opts['network']}") log.info("Rounds: #{opts['rounds']}, threads: #{opts['threads']}, pool: #{opts['pool']}, batch: #{opts['batch']}") start = Time.now round.update round.prepare opts['rounds'].times do |r| round.send round.pull round.match log.info(summary) round.list if (r % 10).zero? end s = Time.now loop do break if Time.now > s + opts['wait'] break if air.fetch.empty? round.pull round.match log.info(summary) end round.list unless air.fetch.empty? air.fetch.each do |p| log.info(" #{p[:source]} -> #{p[:target]} #{p[:amount]} #{Zold::Age.new(p[:pushed])}") end raise "#{air.fetch.count} payments out of #{stats.total('paid')} are still somewhere, we lost them :(" end log.info("Successfully sent and received #{Rainbow(stats.total('arrived')).green} transactions \ in #{Zold::Age.new(start)}") rescue StandardError => ex log.error(Backtrace.new(ex)) exit(-1) end