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 +4 -4
- data/README.md +7 -3
- data/exe/universa +1 -1
- data/lib/universa/client.rb +228 -0
- data/lib/universa/contract.rb +2 -1
- data/lib/universa/errors.rb +4 -0
- data/lib/universa/umi.rb +3 -1
- data/lib/universa/version.rb +1 -1
- data/lib/universa.rb +1 -0
- data/universa.gemspec +3 -0
- metadata +45 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff6ba5e26ea3e8ef5c6f66da7c2e13045f752e251ff8783b990376b06f95af31
|
4
|
+
data.tar.gz: 543afbaaeb3813cfe082171469ef6997dfcd67ad96de62d434556c673d62d198
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
@@ -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
|
data/lib/universa/contract.rb
CHANGED
data/lib/universa/errors.rb
CHANGED
@@ -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
|
data/lib/universa/version.rb
CHANGED
data/lib/universa.rb
CHANGED
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
|
+
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-
|
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
|