class Nanook # The Nanook::Node class contains methods to manage your nano # node and query its data of the nano network. # # Your node is constantly syncing data with other nodes on the network. When # your node first starts up after being built, its database will be empty # and it will begin synchronizing and downloading data of the nano ledger # to its local database. The ledger is the central record of all accounts # and transactions. Some of the methods in this class query your node's # database formed from the nano ledger, and so the responses are determined # by the completeness of your node's database. # # You can determine how synchronized your node is with the nano ledger # with the {#sync_progress} method. # # === Initializing # # Initialize this class through the convenient {Nanook#node} method: # # node = Nanook.new.node # # Or compose the longhand way like this: # # rpc_conn = Nanook::Rpc.new # node = Nanook::Node.new(rpc_conn) class Node def initialize(rpc) @rpc = rpc end # The number of accounts in the nano ledger--essentially all # accounts with _open_ blocks. An _open_ block # is the type of block written to the nano ledger when an account # receives its first payment (see {Nanook::WalletAccount#receive}). All accounts # that respond +true+ to {Nanook::Account#exists?} have open blocks in the ledger. # # @return [Integer] number of accounts with _open_ blocks. def account_count rpc(:frontier_count)[:count] end alias_method :frontier_count, :account_count # The count of all blocks downloaded to the node, and # blocks still to be synchronized by the node. # # ==== Example: # # # # @return [Hash{Symbol=>Integer}] number of blocks and unchecked # synchronizing blocks def block_count rpc(:block_count) end # The count of all known blocks by their type. # # ==== Example: # # node.block_count_by_type # # Example response: # # { # send: 1000, # receive: 900, # open: 900, # change: 50 # } # # @return [Hash{Symbol=>Integer}] number of blocks by type def block_count_by_type rpc(:block_count_type) end alias_method :block_count_type, :block_count_by_type # Initialize bootstrap to a specific IP address and port. # # @return [Boolean] indicating if the action was successful def bootstrap(address:, port:) rpc(:bootstrap, address: address, port: port).has_key?(:success) end # Initialize multi-connection bootstrap to random peers # # @return [Boolean] indicating if the action was successful def bootstrap_any rpc(:bootstrap_any).has_key?(:success) end # Initialize lazy bootstrap with given block hash # # @param hash [String] # @param force [Boolean] False by default. Manually force closing # of all current bootstraps # @return [Boolean] indicating if the action was successful def bootstrap_lazy(hash, force: false) rpc(:bootstrap_lazy, hash: hash, force: force)[:started] == 1 end # Returning status of current bootstrap attempt for debug purposes only. # This call is for internal diagnostics/debug purposes only. # Do not rely on this interface being stable and do not use in a # production system. # # ==== Example: # # node.bootstrap_status # # Example response: # # { # clients: 5790, # pulls: 141065, # pulling: 3, # connections: 16, # idle: 0, # target_connections: 64, # total_blocks: 536820, # lazy_mode: true, # lazy_blocks: 423388, # lazy_state_unknown: 2, # lazy_balances: 0, # lazy_pulls: 0, # lazy_stopped: 644, # lazy_keys: 449, # lazy_key_1: "A86EB2B479AAF3CD531C8356A1FBE3CB500DFBF5BF292E5E6B8D1048DE199C32" # } # # @return [Hash{Symbol=>String|Integer|Boolean}] def bootstrap_status rpc(:bootstrap_status) end # This call is for internal diagnostics/debug purposes only. # Do not rely on this interface being stable and do not use in a # production system. # # Returns block and tally weight (in raw) election duration (in # milliseconds), election confirmation timestamp for recent elections # winners. # # ==== Example: # # node.confirmation_history # # Example response: # # [ # { # block: "EA70B32C55C193345D625F766EEA2FCA52D3F2CCE0B3A30838CC543026BB0FEA", # tally: 80394786589602980996311817874549318248, # duration: 4000, # time: 1544819986, # }, # { # block: "F2F8DA6D2CA0A4D78EB043A7A29E12BDE5B4CE7DE1B99A93A5210428EE5B8667", # tally: 68921714529890443063672782079965877749, # duration: 6000, # time: 1544819988, # } # ] # # @return [Hash{Symbol=>String|Integer}] def confirmation_history rpc(:confirmation_history)[:confirmations].map do |history| # Rename hash key to block block = history.delete(:hash) {block: block}.merge(history) end end # Returns the difficulty values (16 hexadecimal digits string, 64 bit) # for the minimum required on the network (network_minimum) as well # as the current active difficulty seen on the network (network_current, # 5 minute trended average of adjusted difficulty seen on confirmed # transactions) which can be used to perform rework for better # prioritization of transaction processing. A multiplier of the # network_current from the base difficulty of network_minimum is also # provided for comparison. # # ==== Example: # # node.difficulty(include_trend: true) # # Example response: # # { # network_minimum: "ffffffc000000000", # network_current: "ffffffc1816766f2", # multiplier: 1.024089858417128, # difficulty_trend: [ # 1.156096135149775, # 1.190133894573061, # 1.135567138563921, # 1.000000000000000, # ] # } # # @param include_trend [Boolean] false by default. Also returns the # trend of difficulty seen on the network as a list of multipliers. # Sampling occurs every 16 to 36 seconds. The list is ordered such # that the first value is the most recent sample. # @return [Hash{Symbol=>String|Float|Array}] def difficulty(include_trend: false) rpc(:active_difficulty, include_trend: include_trend).tap do |response| response[:multiplier] = response[:multiplier].to_f if response.key?(:difficulty_trend) response[:difficulty_trend].map!(&:to_f) end end end # @return [String] def inspect "#{self.class.name}(object_id: \"#{"0x00%x" % (object_id << 1)}\")" end def peers rpc(:peers)[:peers] end # All representatives and their voting weight. # # ==== Example: # # node.representatives # # Example response: # # { # nano_1111111111111111111111111111111111111111111111111117353trpda: 3822372327060170000000000000000000000, # nano_1111111111111111111111111111111111111111111111111awsq94gtecn: 30999999999999999999999999000000, # nano_114nk4rwjctu6n6tr6g6ps61g1w3hdpjxfas4xj1tq6i8jyomc5d858xr1xi: 0 # } # # @return [Hash{Symbol=>Integer}] known representatives and their voting weight def representatives(unit: Nanook.default_unit) unless Nanook::UNITS.include?(unit) raise ArgumentError.new("Unsupported unit: #{unit}") end response = rpc(:representatives)[:representatives] return response if unit == :raw r = response.map do |account_id, balance| balance = Nanook::Util.raw_to_NANO(balance) [account_id, balance] end Hash[r].to_symbolized_hash end # All online representatives that have voted recently. Note, due to the # design of the nano RPC, this method cannot return the voting weight # of the representatives. # # ==== Example: # # node.representatives_online # => ["nano_111...", "nano_222"] # # @return [Array] array of representative account ids def representatives_online rpc(:representatives_online)[:representatives].keys.map(&:to_s) end # Safely shuts down the node. # # @return [Boolean] indicating if action was successful def stop rpc(:stop).has_key?(:success) end # @param limit [Integer] number of synchronizing blocks to return # @return [Hash{Symbol=>String}] information about the synchronizing blocks for this node def synchronizing_blocks(limit: 1000) response = rpc(:unchecked, count: limit)[:blocks] response = response.map do |block, info| [block, JSON.parse(info).to_symbolized_hash] end Hash[response.sort].to_symbolized_hash end alias_method :unchecked, :synchronizing_blocks # The percentage completeness of the synchronization process for # your node as it downloads the nano ledger. Note, it's normal for # your progress to not ever reach 100. The closer to 100, the more # complete your node's data is, and so the query methods in this class # become more reliable. # # @return [Float] the percentage completeness of the synchronization # process for your node def sync_progress response = rpc(:block_count) count = response[:count] unchecked = response[:unchecked] total = count + unchecked count.to_f * 100 / total.to_f end # Returns node uptime in seconds # # @return [Integer] seconds of uptime def uptime rpc(:uptime)['seconds'] end # This method is deprecated and will be removed in 3.0, as a node never # reaches 100% synchronization. # # @return [Boolean] signalling if this node ever reaches 100% synchronized def synced? warn "[DEPRECATION] `synced?` is deprecated and will be removed in 3.0" rpc(:block_count)[:unchecked] == 0 end # @return [Hash{Symbol=>Integer|String}] version information for this node def version rpc(:version) end alias_method :info, :version private def rpc(action, params={}) @rpc.call(action, params) end end end