universa 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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