class Universa::Client

The universa network client. Discover and connects to the universa network, provides consensus operations and all other whole-network related functions.

Attributes

private_key[R]

Client private key ised in the connection

size[R]

Discovered network size

Public Class Methods

new(topology: "mainnet", private_key: PrivateKey.new(2048), cache_dir: nil) click to toggle source

Construct an Universa network client. Bu default, connects to the main network. Perform consensus-based network scanning and saves the current network topology in the cache on the file system, default is under +~/.universa+ but could be overriden.

If the network topology file is presented but the cached topology is newer, the cached will be used.

The client accepts small network topology changes as long as it still create consensus. Still, too big changes in the network topology might require fresh topology file (or upgrade the gem).

@param [String] topology could be name of known network (e.g. mainnet as by default) or path to a .json file

containing some network topology, for example, obtained from some external source like telegram
channel.

@param [PrivateKey] private_key to connect with. @param [String] cache_dir where to store resulting topology. we recommend to leave it as nil.

@raise if network topology could not be checked/obtained.

# File lib/universa/client.rb, line 43
def initialize topology: "mainnet", private_key: PrivateKey.new(2048), cache_dir: nil
  @client = UmiClient.new topology, cache_dir, private_key
  @private_key = PrivateKey
  @size = @client.size
  @connections = (0...@size).map {nil}
end

Public Instance Methods

[](index) click to toggle source

Get the node connection by its index (0…size). @return [Connection] object

# File lib/universa/client.rb, line 52
def [] index
  raise IndexError if index < 0 || index >= @size
  @connections[index] ||= Connection.new(@client.getClient(index))
end
get_state(obj, trust: 0.3) click to toggle source

Perform fast consensus state check with a given trust level, as the fraction of the whole network size. It checks the network nodes randomly until get enough positive or negative states. The lover the required trust level is, the faster the answer will be found.

@param [Contract | HashId] obj contract to check @param [Object] trust level, should be between 0.1 (10% of network) and 0.9 (90% of the network) @return [ContractState] of some final node check It does not calculates average time (yet)

# File lib/universa/client.rb, line 100
  def get_state obj, trust: 0.3
  raise ArgumentError, "trusst must be in 0.1..0.9 range" if trust < 0.1 || trust > 0.9
  result = Concurrent::IVar.new
  negative_votes = Concurrent::AtomicFixnum.new((size * 0.1).round + 1)
  positive_votes = Concurrent::AtomicFixnum.new((size * trust).round)

  # consensus-finding conveyor: we chek connections in batches in parallel until get
  # some consensus. We do not wait until all of them will answer
  (0...size).to_a.shuffle.each {|index|
    Thread.start {
      if result.incomplete?
        if (state = self[index].get_state(obj)).approved?
          result.try_set(state) if positive_votes.decrement < 0
        else
          result.try_set(state) if negative_votes.decrement < 0
        end
      end
    }
  }
  result.value
end
is_approved?(obj, trust: 0.3) click to toggle source

Perform fast consensus state check with a given trust level, determining whether the item is approved or not. Blocks for 1 minute or until the network solution will be collected for a given trust level.

@param [Contract | HashId | String | Binary] obj contract to check @param [Object] trust level, should be between 0.1 (10% of network) and 0.9 (90% of the network) @return true if the contract state is approved by the network with a given trust level, false otherwise.

# File lib/universa/client.rb, line 76
def is_approved? obj, trust: 0.3
  hash_id = case obj
              when HashId
                obj
              when Contract
                obj.hash_id
              when String
                if obj.encoding == Encoding::ASCII_8BIT
                  HashId.from_digest(obj)
                else
                  HashId.from_string(obj)
                end
              else
                raise ArgumentError "wrong type of object to check approval"
            end
  @client.isApprovedByNetwork(hash_id, trust.to_f, 60000)
end
random_connection() click to toggle source

Get the random node connection @return [Connection] node connection

# File lib/universa/client.rb, line 59
def random_connection
  self[rand(0...size)]
end
random_connections(number) click to toggle source

Get several random connections @param [Numeric] number of connections to get @return [Array(Connection)] array of connections to random (non repeating) nodes

# File lib/universa/client.rb, line 66
def random_connections number
  (0...size).to_a.sample(number).map {|n| self[n]}
end
register_single(contract, timeout: 45, max_retries: 3) click to toggle source

Register a single contract (on private network or if you have white key allowing free operations) on a random node. Client must check returned contract state. It requires “open” network or special key that has a right to register contracts without payment.

When retrying, randpm nodes are selected.

@param [Contract] contract must be sealed ({Contract#seal})

@return [ContractState] of the result. Could contain errors.

# File lib/universa/client.rb, line 131
def register_single(contract, timeout: 45, max_retries: 3)
  retry_with_timeout(timeout, max_retries) {
    ContractState.new(random_connection.register_single(contract, timeout / max_retries * 1000 - 100))
  }
end