universa 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9fb1beaf2ba6ae53212fa913c1a7fddc49c6484871a3f0c54c62c3b561284fdf
4
- data.tar.gz: 9f11ec4e2a52e6abfd78161f75fed790b7f32cd5348b2cf321f62c18d253d501
3
+ metadata.gz: ff6ba5e26ea3e8ef5c6f66da7c2e13045f752e251ff8783b990376b06f95af31
4
+ data.tar.gz: 543afbaaeb3813cfe082171469ef6997dfcd67ad96de62d434556c673d62d198
5
5
  SHA512:
6
- metadata.gz: 1928165a250af830aa2768d728b6ef41662c57604209db3926af675eff13e8c2e1e23c116af7f291bd6555b4c8e1236f263aea2a2906ef0c4d75a2f7d8c5b5a2
7
- data.tar.gz: 5964bc287b924b9d0b30d91d32a0ba4c43ef946f4fb8958914582f2ac6964fabdd446fe8a7898561d8b36973a78cfb5b8fbe52050d9cf95efba328987c41259d
6
+ metadata.gz: 576c85a3a975de182195866635820dcbbb2a1dc531526440aee8dbb8d35d4783db45b2cec8b0c1ac7908caaa7b57de7c4bc893412dee60e84425bd047511e422
7
+ data.tar.gz: 677b361b35bcfe73ed8005735123ab6eea59a3077d41646a3dbccea9bd2885169d416dcf7f7929128e46e64347b4e0d9780c4732d9470e13985b839d36101241
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Universa
2
2
 
3
- > Alfa state: direct access to Java API ready for test.
3
+ > Alfa state: direct full access to Java API ready for test, started adapter of remote objects - syntax sugar
4
+ for direct access to remote objects.
4
5
 
5
6
  This is an under-construction official gem from [Universa][universa] to facilitate access to the
6
7
  Java library using Universa's UMI protocol.
@@ -50,11 +51,14 @@ contract_id = contract.get_id.to_base64_string
50
51
 
51
52
  ```
52
53
 
54
+ ## Docs and resources
55
+
53
56
  for more information see:
54
57
 
55
58
  - [Universa gem page](https://kb.universa.io/universa_ruby_gem/131) in the Universa Knowledge Base.
56
59
  - Universa Java API: https://kb.universa.io/general_java_api/5
57
60
  - Universa UMI server: https://kb.universa.io/umi_protocol/98
61
+ - Ruby [docs online](https://kb.universa.io/system/static/gem_universa/)
58
62
  - Farcall [gem](https://github.com/sergeych/farcall) and [protocol](https://github.com/sergeych/farcall/wiki).
59
63
 
60
64
  ### Use UMI service (alfa state)
@@ -63,7 +67,7 @@ The Universa::Service greatly simplify work taking all boilerplate. Just create
63
67
  as is they are local:
64
68
 
65
69
  ```ruby
66
- key = PrivateKey,new(open('mykey.private.unikey', 'rb').read)
70
+ key = PrivateKey.new(open('mykey.private.unikey', 'rb').read)
67
71
  contract = Contract.new(key)
68
72
 
69
73
  contract.seal()
@@ -91,4 +95,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
91
95
 
92
96
  Everyone interacting in the Universa project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/universa/blob/master/CODE_OF_CONDUCT.md).
93
97
 
94
- [universa]:http://universa.io
98
+ [universa]:http://universa.io
data/exe/universa CHANGED
@@ -5,5 +5,5 @@ require "universa"
5
5
 
6
6
  puts "Checking prerequisites"
7
7
 
8
- puts "Found UMI version: #{UMI.new.version}"
8
+ puts "Found UMI version: #{Universa::UMI.new.version}"
9
9
 
@@ -0,0 +1,228 @@
1
+ require 'boss-protocol'
2
+ require 'open-uri'
3
+ require 'concurrent'
4
+
5
+ module Universa
6
+
7
+ using Universa
8
+
9
+ def retry_with_timeout(max_timeout = 15, max_times = 3, &block)
10
+ attempt = 0
11
+ Timeout::timeout(max_timeout, &block)
12
+ rescue
13
+ attempt += 1
14
+ puts "timeout: retry (#$!): #{attempt}"
15
+ retry if attempt < max_times
16
+ raise
17
+ end
18
+
19
+
20
+ module Parallel
21
+
22
+ class ParallelEnumerable < SimpleDelegator
23
+ include Concurrent
24
+
25
+ @@pool = CachedThreadPool.new
26
+
27
+ def each_with_index &block
28
+ latch = CountDownLatch.new(size)
29
+ __getobj__.each_with_index {|x, index|
30
+ @@pool << -> {
31
+ begin
32
+ block.call(x, index)
33
+ rescue
34
+ $!.print_stack_trace
35
+ ensure
36
+ latch.count_down
37
+ end
38
+ }
39
+ }
40
+ latch.wait
41
+ end
42
+
43
+ def each &block
44
+ each_with_index {|x, i| block.call(x)}
45
+ end
46
+
47
+
48
+ def map &block
49
+ result = size.times.map {nil}
50
+ each_with_index {|value, i|
51
+ result[i] = block.call(value)
52
+ }
53
+ result.par
54
+ end
55
+
56
+ alias_method :collect, :map
57
+ end
58
+
59
+ refine Enumerable do
60
+ def par
61
+ is_a?(ParallelEnumerable) ? self : ParallelEnumerable.new(self)
62
+ end
63
+
64
+ def group_by &block
65
+ result = {}
66
+ each {|value|
67
+ new_key = block.call(value)
68
+ (result[new_key] ||= []) << value
69
+ }
70
+ result
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+
77
+ using Parallel
78
+
79
+ # Universa network client reads current network configuration and provides access to each node independently
80
+ # and also implement newtor-wide procedures.
81
+ class Client
82
+
83
+ # Create client
84
+ # @param [PrivateKey] private_key to connect with. Generates new one if omitted.
85
+ def initialize private_key = nil
86
+ @connection_key = private_key
87
+ scan_network()
88
+ end
89
+
90
+ # Number of accessible nodes
91
+ def size
92
+ @nodes.size
93
+ end
94
+
95
+ # private key used by the connection (might be generated)
96
+ def private_key
97
+ @connection_key ||= PrivateKey.new(2048)
98
+ end
99
+
100
+ # @return [Connection] random connection
101
+ def random_connection
102
+ @nodes.sample
103
+ end
104
+
105
+ # @return [Array(Connection)] array of count randomly selected connections
106
+ def random_connections count = 1
107
+ @nodes.sample(count)
108
+ end
109
+
110
+
111
+ private
112
+
113
+ # Rescan the network collecting the networ map comparing results from random 70% of nodes.
114
+ def scan_network
115
+ # Todo: cache known nodes
116
+ root_nodes = (1..30).map {|n| "http://node-#{n}-com.universa.io:8080/network"}
117
+
118
+ # We scan random 70% for consensus
119
+ n = root_nodes.size * 0.7
120
+
121
+ candidates = {}
122
+ root_nodes.sample(n).par.each {|path|
123
+ retry_with_timeout(5, 3) {
124
+ SmartHash.new(Boss.unpack open(path).read).response.nodes.each {|data|
125
+ ni = NodeInfo.new(data)
126
+ (candidates[ni] ||= ni).increment_rate
127
+ }
128
+ }
129
+ }
130
+ nodes = candidates.values.group_by(&:url)
131
+ .transform_values!(&:sort)
132
+ # We roughly assume the full network size as:
133
+ network_max_size = nodes.size
134
+ nodes.each {|k, v|
135
+ puts "#{k}: (#{v.size}) #{v.map(&:rate)}}"
136
+ }
137
+ # Refine result: takes most voted nodes and only these with 80% consensus
138
+ # and map it to Connection objects
139
+ min_rate = n * 0.8
140
+ @nodes = nodes.values.map {|v| v[-1]}.delete_if {|v| v.rate < min_rate}
141
+ .map {|ni| Connection.new(self, ni)}
142
+ raise NetworkError, "network is not ready" if @nodes.size < network_max_size * 0.9
143
+ end
144
+ end
145
+
146
+ # The node information
147
+ class NodeInfo
148
+ attr :number, :packed_key, :url
149
+
150
+ def initialize(data)
151
+ @data, @number, @url, @packed_key = data, data.number, data.url, data.packed_key
152
+ @rate = Concurrent::AtomicFixnum.new
153
+ end
154
+
155
+ def rate
156
+ @rate.value
157
+ end
158
+
159
+ def increment_rate
160
+ @rate.increment
161
+ end
162
+
163
+ def == other
164
+ # number == other.number && packed_key == other.packed_key && url == other.url
165
+ url == other&.url && packed_key == other&.packed_key && url == other&.url
166
+ end
167
+
168
+ def hash
169
+ @url.hash + @packed_key.hash
170
+ end
171
+
172
+ def eql?(other)
173
+ self == other
174
+ end
175
+
176
+ def < other
177
+ rate < other.rate
178
+ end
179
+ end
180
+
181
+
182
+ # Access to the single node using universa client protocol.
183
+ #
184
+ class Connection
185
+ # create connection for a given clietn. Don't call it direcly, use
186
+ # {Client.random_connection} or {Client.random_connections} instead. The client implements
187
+ # lazy initialization so time-consuming actual connection will be postponed until
188
+ # needed.
189
+ #
190
+ # @param [Client] client instance to be bound to
191
+ # @param [NodeInfo] node_info to connect to
192
+ def initialize(client, node_info)
193
+ @client, @node_info = client, node_info
194
+ end
195
+
196
+ # executes ping. Just to ensure connection is alive. Node answers 'sping' => 'spong' hash.
197
+ # 's' states that secure layer of client protocol is used, e.g. with mutual identification and
198
+ # ciphering.
199
+ def ping
200
+ execute(:sping)
201
+ end
202
+
203
+ # Execute Universa Node client protocol command with optional keyword arguments that will be passed
204
+ # to the node.
205
+ #
206
+ # @param [String|Symbol] name of the command
207
+ # @return [SmartHash] with the command result
208
+ def execute name, **kwargs
209
+ Service.umi.with_trace {
210
+ connection.command name.to_s, *kwargs.to_a.flatten
211
+ }
212
+ end
213
+
214
+ protected
215
+
216
+ def connection
217
+ @connection ||= retry_with_timeout(15, 3) {
218
+ Service.umi.instantiate "com.icodici.universa.node2.network.Client",
219
+ @node_info.url,
220
+ @client.private_key,
221
+ nil,
222
+ false
223
+ }
224
+ end
225
+
226
+ end
227
+
228
+ end
@@ -12,9 +12,10 @@ module Universa
12
12
 
13
13
  # returns keys that will be used to sign this contract on next {seal}.
14
14
  # @return [Set<PrivateKey>] set of private keys
15
- def signing_keys
15
+ def keys_to_sign_with
16
16
  get_keys_to_sign_with
17
17
  end
18
+
18
19
  end
19
20
 
20
21
  end
@@ -4,6 +4,10 @@ module Universa
4
4
  class Error < IOError
5
5
  end
6
6
 
7
+ # Genegal error with universa network
8
+ class NetworkError < Error
9
+ end
10
+
7
11
  # references from different {UMI} instances are mixed together
8
12
  class InterchangeError < Error
9
13
  # create instance optionally overriding message
data/lib/universa/umi.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'open3'
2
2
  require 'farcall'
3
+ require 'set'
3
4
  require 'base64'
4
5
  require 'weakref'
5
6
  require_relative 'weak_reference'
@@ -137,8 +138,9 @@ module Universa
137
138
  # These calls could be nested, on exit it restores previous trace state
138
139
  def with_trace &block
139
140
  current_state, @trace = @trace, true
140
- block.call()
141
+ result = block.call()
141
142
  @trace = current_state
143
+ result
142
144
  end
143
145
 
144
146
  private
@@ -1,4 +1,4 @@
1
1
  module Universa
2
2
  # Current gem version
3
- VERSION = "0.1.4"
3
+ VERSION = "0.1.5"
4
4
  end
data/lib/universa.rb CHANGED
@@ -6,6 +6,7 @@ require "universa/umi"
6
6
  require "universa/service"
7
7
  require "universa/keys"
8
8
  require "universa/contract"
9
+ require "universa/client"
9
10
 
10
11
  # The Universa gem
11
12
  #
data/universa.gemspec CHANGED
@@ -31,6 +31,9 @@ Gem::Specification.new do |spec|
31
31
  spec.require_paths = ["lib"]
32
32
 
33
33
  spec.add_dependency "farcall", ">= 0.4.6"
34
+ spec.add_dependency "boss-protocol", ">= 1.5.0"
35
+ spec.add_dependency "concurrent-ruby", ">= 1.0.5"
36
+ spec.add_dependency "concurrent-ruby-ext"
34
37
 
35
38
  spec.add_development_dependency "bundler", "~> 1.16"
36
39
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: universa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - sergeych
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-03 00:00:00.000000000 Z
11
+ date: 2018-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: farcall
@@ -24,6 +24,48 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.4.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: boss-protocol
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.5.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: concurrent-ruby
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.5
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: concurrent-ruby-ext
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: bundler
29
71
  requirement: !ruby/object:Gem::Requirement
@@ -111,6 +153,7 @@ files:
111
153
  - bin/umi/lib/org.yaml.snakeyaml-1.18.jar
112
154
  - exe/universa
113
155
  - lib/universa.rb
156
+ - lib/universa/client.rb
114
157
  - lib/universa/contract.rb
115
158
  - lib/universa/errors.rb
116
159
  - lib/universa/keys.rb