universa 0.1.9 → 0.2.1

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: 3393ecdb08b1b7fe37288be48d86b47a740cf7e923575caa45295c8cdb861247
4
- data.tar.gz: 76a9a13bdf1a39c8d5e9a0e838a3faf18283d9b357cd3bca18c75f076dbcc65f
3
+ metadata.gz: ff86a735f983f4cb94e6ca82be229c8c47749d975483fe52d11972a8664853d8
4
+ data.tar.gz: f1625ae689ba6bd1ff017c2a3926e16e3b6efcc71f90f7896b60ccce19839413
5
5
  SHA512:
6
- metadata.gz: 44c450550570eb0e447f7824dae25129cfd59ead09cac86c19b8d3b261d226d7964b88ca3304fc19790ca7512509c5ae3575c0aed09da608133de588d1d1548c
7
- data.tar.gz: 5b093f28766e602653ad3c9797f4ce6d6a63796c8cd45ab9252b4bd78e145a43aa9510c51370d210d23a97eeab7d4cd14049466f3bc3de0fd933db3b3c1a3467
6
+ metadata.gz: fd4b45a562f5340511e659fb1019642599b74d431cdb5c8277a168e014ac1c085518798232d6c156de43377a3bdb3eec819ff935518bd107aef65726515f60db
7
+ data.tar.gz: 00a4544555433540ca9317ee4ce3bb7a090c649b5489809b364e79a36d784d4905f2af5b42534ade5b79b809ba19fe1d98a63ea784aff4cb19bf0aeb3af514ca
data/README.md CHANGED
@@ -6,6 +6,18 @@ for direct access to remote objects.
6
6
  This is an under-construction official gem from [Universa][universa] to facilitate access to the
7
7
  Java library using Universa's UMI protocol.
8
8
 
9
+ ## News
10
+
11
+ - alfa version of the local FS-based contract store.
12
+ - ability to edit `contract.state` and `contract.transactional` in new revisions.
13
+ - fixed errors with interchange builder and set based objects
14
+ - ruby sugar for Universa native classes: Role, Adapter, Contract, Binder, HashId.
15
+ - Contract creation, revocation, changing owner in ruby way
16
+ - Network operation for white keys/private networks: parallel state check, contract recistration.
17
+
18
+ This gem is already used in new Universa projects and is being actively tested.
19
+
20
+
9
21
  ## Installation
10
22
 
11
23
  ### Prerequisites
@@ -8,6 +8,9 @@ require "universa/keys"
8
8
  require "universa/binder"
9
9
  require "universa/contract"
10
10
  require "universa/client"
11
+ require 'universa/stored_contract'
12
+ require "universa/chain_store"
13
+ require "universa/fs_store/file_store"
11
14
 
12
15
  # The Universa gem
13
16
  #
@@ -68,8 +68,8 @@ module Universa
68
68
  end
69
69
 
70
70
  # @return an array of values returned by the block
71
- # @yiekd [key,value] pairs.
72
- def map &block
71
+ # @yield [key,value] pairs.
72
+ def map(&block)
73
73
  keys.map {|k| block.call [k, __getobj__.get(k)]}
74
74
  end
75
75
 
@@ -86,7 +86,13 @@ module Universa
86
86
 
87
87
  end
88
88
 
89
+ # enhance Hash with UMI access convenience methods.
89
90
  class Hash
91
+ # Convert the hash to the {}Binder} stored in the remote side and suitable for
92
+ # Universa UMI calls.
93
+ #
94
+ # @return [Binder] constructed reference to the remotely created Binder.
95
+ #
90
96
  def to_binder
91
97
  Binder.of self
92
98
  end
@@ -0,0 +1,54 @@
1
+ require 'yaml'
2
+
3
+ module Universa
4
+
5
+ # The storage interface capable to store contracts in chains, providing search and attributes.
6
+ # This class is not a store itself but the base class for it, having common boilerplate and
7
+ # sort of interface to implement.
8
+ class ChainStore
9
+
10
+ # Save contract to the store. When this method returns, the contract must me already stored.
11
+ # If the contract with such hasId is already stored, just returns it.
12
+ #
13
+ # @param [Object] contract to store
14
+ # @return [StoredContract] for this contract
15
+ def store_contract(contract)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ # Same as {#store_contract} but returns store
20
+ # @param [Contract] contract to add
21
+ # @return [ChainStore] self
22
+ def <<(contract)
23
+ store_contract(contract)
24
+ self
25
+ end
26
+
27
+ # @return [Contract] with the corresponding id or nil
28
+ # @param [HashId] hash_id instance to look for
29
+ def find_by_id(hash_id)
30
+ raise NotImplementedError
31
+ end
32
+
33
+ # Count contracts in the store. This operation could be slow.
34
+ def count
35
+ raise NotImplementedError
36
+ end
37
+
38
+ # @return [Contract] with the corresponding id or raise.
39
+ # @param [HashId] hash_id instance to look for
40
+ # @raise [NotFoundError]
41
+ def find_by_id! hash_id
42
+ find_by_id(hash_id) or raise NotFoundError
43
+ end
44
+
45
+ # Find all contracts with this parent id.
46
+ # @param [HashId] hash_id of the parent contract
47
+ # @return [Array] all the contracts that match this criterion
48
+ def find_by_parent(hash_id)
49
+ raise NotImplementedError
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -172,9 +172,9 @@ module Universa
172
172
  # Register a single contract (on private network or if you have white key allowing free operations)
173
173
  # on a single node.
174
174
  #
175
- # @param [Contract] contract, muts be sealed ({Contract#seal})
175
+ # @param [Contract] contract must be sealed ({Contract#seal})
176
176
  # @return [ContractState] of the result. Could contain errors.
177
- def register_single contract
177
+ def register_single(contract)
178
178
  retry_with_timeout(15, 3) {
179
179
  result = ContractState.new(execute "approve", packedItem: contract.packed)
180
180
  while result.is_pending
@@ -205,8 +205,9 @@ module Universa
205
205
  # to the node.
206
206
  #
207
207
  # @param [String|Symbol] name of the command
208
+ # @param kwargs arguments to call
208
209
  # @return [SmartHash] with the command result
209
- def execute name, **kwargs
210
+ def execute(name, **kwargs)
210
211
  connection.command name.to_s, *kwargs.to_a.flatten
211
212
  end
212
213
 
@@ -34,11 +34,12 @@ module Universa
34
34
  invoke_static 'with_digest', digest_bytes
35
35
  end
36
36
 
37
- # Construct from string representation of the ID, not to confuse with binary one.
37
+ # Construct from string representation of the ID, not to confuse with binary one. This method takes both
38
+ # regular base64 representation and RFC3548 url-safe modification, as from {#to_url_safe_string}.
38
39
  #
39
40
  # @param [String] string_id id string representation, like from +hash_id_instance.to_s+. See {#to_s}.
40
41
  def self.from_string(string_id)
41
- string_id.force_encoding 'utf-8'
42
+ string_id.force_encoding('utf-8').gsub('-','+').gsub('_','/')
42
43
  invoke_static 'with_digest', string_id
43
44
  end
44
45
 
@@ -57,6 +58,28 @@ module Universa
57
58
  def to_s
58
59
  Base64.encode64(get_digest).gsub(/\s/, '')
59
60
  end
61
+
62
+ # Converts to URL-safe varianot of base64, as RFC 3548 suggests:
63
+ # the 63:nd / character with the underscore _
64
+ # the 62:nd + character with the minus -
65
+ #
66
+ # Could be decoded safely back with {HashId.from_string} but not (most likely) with JAVA API itself
67
+ # @return [String] RFC3548 modified base64
68
+ def to_url_safe_string
69
+ Base64.encode64(get_digest).gsub(/\s/, '').gsub('/','_').gsub('+', '-')
70
+ end
71
+
72
+ # To use it as a hash key_address.
73
+ # @return hash calculated over the digest bytes
74
+ def hash
75
+ bytes.hash
76
+ end
77
+
78
+ # To use it as a hash key_address. Same as this == other.
79
+ def eql? other
80
+ self == other
81
+ end
82
+
60
83
  end
61
84
 
62
85
  # Universa contract adapter.
@@ -66,17 +89,17 @@ module Universa
66
89
  # Create simple contract with preset critical parts:
67
90
  #
68
91
  # - expiration set to 90 days unless specified else
69
- # - issuer role is set to the address of the issuer key, short ot long
92
+ # - issuer role is set to the address of the issuer key_address, short ot long
70
93
  # - creator role is set as link to issuer
71
94
  # - owner role is set as link to issuer
72
95
  # - change owner permission is set to link to owner
73
96
  #
74
- # The while contract is then signed by the issuer key. Not that it will not seal it: caller almost always
97
+ # The while contract is then signed by the issuer key_address. Not that it will not seal it: caller almost always
75
98
  # will add more data before it, then must call #seal().
76
99
  #
77
100
  # @param [PrivateKey] issuer_key also will be used to sign it
78
101
  # @param [Time] expires_at defaults to 90 days
79
- # @param [Boolean] use_short_address set to true to use short address of the issuer key in the role
102
+ # @param [Boolean] use_short_address set to true to use short address of the issuer key_address in the role
80
103
  # @return [Contract] simple contact, not sealed
81
104
  def self.create issuer_key, expires_at: (Time.now + 90 * 24 * 60 * 60), use_short_address: false
82
105
  contract = Contract.new
@@ -92,6 +115,7 @@ module Universa
92
115
 
93
116
  # Load from transaction pack
94
117
  def self.from_packed packed
118
+ packed.nil? and raise ArgumentError, "packed contract required"
95
119
  packed.force_encoding 'binary'
96
120
  self.invoke_static "fromPackedTransaction", packed
97
121
  end
@@ -124,8 +148,10 @@ module Universa
124
148
  get_owner
125
149
  end
126
150
 
127
- def owner= key
128
- set_owner_key key
151
+ # Set owner to the key_address, usable only in the simplest case where owner is the single address.
152
+ # @param [KeyAddress | PublicKey] key_address
153
+ def owner=(key_address)
154
+ set_owner_key key_address
129
155
  end
130
156
 
131
157
  # Shortcut for is_ok
@@ -134,16 +160,29 @@ module Universa
134
160
  end
135
161
 
136
162
  # shortcut for getHashId
163
+ # @return [HashId] of the contracr
137
164
  def hash_id
138
- get_id
165
+ getId()
166
+ end
167
+
168
+ # @return [HashId] of the origin contract
169
+ def origin
170
+ getOrigin()
139
171
  end
140
172
 
141
- # shortcut for get_expires_at
173
+ # @return [HashId] pf the parent contracr
174
+ def parent
175
+ getParent()
176
+ end
177
+
178
+ # shortcut for get_expires_at. Get the contract expiration time.
142
179
  def expires_at
143
180
  get_expires_at
144
181
  end
145
182
 
146
- def expires_at= time
183
+ # set +expires_at+ field
184
+ # @param [Time] time when this contract will be expired, if yet +APPROVED+.
185
+ def expires_at=(time)
147
186
  set_expires_at time
148
187
  end
149
188
 
@@ -152,6 +191,7 @@ module Universa
152
191
  @definition ||= get_definition.get_data
153
192
  end
154
193
 
194
+ # Return +state+ binder. Shortcut for Java API +getStateData()+
155
195
  def state
156
196
  @state ||= getStateData()
157
197
  end
@@ -175,7 +215,7 @@ module Universa
175
215
  # Write helper for many token-like contracts containing state.data.amount. Saves value
176
216
  # in state.data.anomount and properly encodes it so it will be preserved on packing.
177
217
  #
178
- # @param [Object] value, should be some representation of a number (also string)
218
+ # @param [Object] value should be some representation of a number (also string)
179
219
  def amount= (value)
180
220
  state[:amount] = value.to_s.force_encoding('utf-8')
181
221
  end
@@ -203,7 +243,11 @@ module Universa
203
243
  getErrors.map {|e| "(#{e.object || ''}): #{e.error}"}.join(', ').strip
204
244
  end
205
245
 
206
- def can_perform_role name, *keys
246
+ # Test that some set of keys could be used to perform some role.
247
+ #
248
+ # @param [String] name of the role to check
249
+ # @param [PublicKey] keys instances to check against
250
+ def can_perform_role(name, *keys)
207
251
  getRole(name.to_s).isAllowedForKeys(Set.new keys.map {|x|
208
252
  x.is_a?(PrivateKey) ? x.public_key : x
209
253
  })
@@ -16,6 +16,16 @@ module Universa
16
16
  end
17
17
  end
18
18
 
19
+ class StoreError < Error;
20
+ end
21
+
22
+ class NotFoundError < StoreError
23
+ end
24
+
25
+ class IllegalStateError < StoreError
26
+
27
+ end
28
+
19
29
  # Easy print stack trace refinement
20
30
  refine Exception do
21
31
 
@@ -0,0 +1,118 @@
1
+ module Universa::FSStore
2
+
3
+ # The {StoredContract} implementation to work with {FileStore}.
4
+ #
5
+ # @!method name
6
+ # @return [String] the +contract.definition.data.name+ value or nil.
7
+ #
8
+ # @!method currency
9
+ # @return [String] the +contract.definition.data.currency+ value or nil.
10
+ #
11
+ # @!method amount
12
+ # @return [BigDecimal] +contract.state.data.amount+ or nil. See {Contract#amount} for more.
13
+ #
14
+ class Entry < Universa::StoredContract
15
+
16
+ extend Forwardable
17
+
18
+ # (see StoredContract#load)
19
+ def load(hash_id)
20
+ init_with_hash_id hash_id
21
+ self
22
+ end
23
+
24
+ # initialize new instance with an existing contract
25
+ # @param [Contract] contract to store
26
+ def init_with_contract(contract)
27
+ self.contract = contract
28
+ self
29
+ end
30
+
31
+ # initialize new instance with attributes YAML file
32
+ # @param [String] file_name of the +.unicon.yaml+ file
33
+ def load_from_yaml_file(file_name)
34
+ init_with_yaml_file_name file_name
35
+ self
36
+ end
37
+
38
+ # (see StoredContract#hash_id)
39
+ def hash_id
40
+ @id
41
+ end
42
+
43
+ # (see StoredContract#contract=)
44
+ def contract= new_contract
45
+ @id = new_contract.hash_id
46
+ prepare_file_names
47
+ super
48
+ # we will always rewrite existing file to be sure it is correct
49
+ open(@file_name, 'wb') {|f| f << contract.packed}
50
+ # now we are to extract and rewrite attributes
51
+ load_attributes_from_contract # it will save them too
52
+ end
53
+
54
+ # Implement lazy load logic
55
+ # @return [Contract] instance loaded at first call only
56
+ def contract
57
+ # load it if it is not
58
+ self.packed_contract = open(@file_name, 'rb') {|f| f.read} unless has_contract?
59
+ # @attributes must already be set
60
+ super
61
+ end
62
+
63
+ def_delegators :@attributes, :name, :currency, :amount
64
+
65
+ protected
66
+
67
+ # initialize instance for an existing file. Should be a contract already stored in the connected store.
68
+ # @param [Object] hash_id to construct from
69
+ def init_with_hash_id(hash_id)
70
+ raise IllegalStateError, "already initialized" if @id
71
+ @id = hash_id
72
+ prepare_file_names
73
+ load_attributes_from_file
74
+ # attrs are already in the file so we need not to save them
75
+ end
76
+
77
+ # Load from attributes file name
78
+ def init_with_yaml_file_name file_name
79
+ @attr_file_name = file_name
80
+ load_attributes_from_file
81
+ prepare_file_names
82
+ end
83
+
84
+ # save attributes to .yaml file
85
+ def save_attributes
86
+ open(@attr_file_name, 'w') {|f| YAML.dump(@attributes, f)}
87
+ end
88
+
89
+ # load attributes from a contract (already assigned) and store them in the .yaml file
90
+ def load_attributes_from_contract
91
+ state = contract.state
92
+ definition = contract.definition
93
+ @attributes = SmartHash.new({
94
+ id: hash_id.to_s,
95
+ parent: parent&.to_s,
96
+ origin: origin.to_s,
97
+ name: definition.name,
98
+ currency: definition.currency,
99
+ amount: state.amount
100
+ })
101
+ save_attributes
102
+ end
103
+
104
+ # load attributes from the .yaml file
105
+ def load_attributes_from_file
106
+ @attributes = SmartHash.new(YAML.load_file(@attr_file_name))
107
+ @id = HashId.from_string @attributes.id
108
+ end
109
+
110
+ # prepare file name fields (@file_name and @attr_file_name)
111
+ # @param [HashId] hash_id or ni to use one already set in @id
112
+ def prepare_file_names(hash_id = nil)
113
+ @file_name = "#{chain_store.root}/#{@id.to_url_safe_string[0..27]}.unicon"
114
+ @attr_file_name = "#@file_name.yaml"
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,63 @@
1
+ require 'forwardable'
2
+ require_relative './entry'
3
+
4
+ # Filesystem-based storage. See {FileStore}.
5
+ module Universa::FSStore
6
+
7
+ # Simple file-based store that could be efficiently user with per-file cloud storages like Dropbox,
8
+ # Google Disk, NextCloud and like.
9
+ #
10
+ # Notes to developers:
11
+ #
12
+ # - attributes are eager loaded: should always be contructed from contract or from file
13
+ # - contract is lazy loaded
14
+ class FileStore < Universa::ChainStore
15
+
16
+ # [String] The file store root path
17
+ attr :root
18
+
19
+ # Construct store in the path supplied. If the path is not empty, it will be scanned for stored contracts.
20
+ # @param [String] root_path of the store, must exist.
21
+ def initialize(root_path)
22
+ @root = root_path
23
+ @root = @root[0...-1] while (@root[-1] == '/')
24
+ init_cache
25
+ end
26
+
27
+ # (see ChainStore#store_contract)
28
+ def store_contract contract
29
+ entry = FSStore::Entry.new(self)
30
+ entry = entry.init_with_contract(contract)
31
+ add_to_cache entry
32
+ end
33
+
34
+ # (see ChainStore#find_by_id)
35
+ def find_by_id hash_id
36
+ @cache[hash_id]
37
+ end
38
+
39
+ # (see ChainStore#count)
40
+ def count
41
+ @cache.size
42
+ end
43
+
44
+ protected
45
+
46
+ # scan the root folder for attribute files and store them in the cache
47
+ def init_cache
48
+ @cache = {}
49
+ Dir[@root + "/*.unicon.yaml"].each {|name|
50
+ add_to_cache Entry.new(self).load_from_yaml_file(name)
51
+ }
52
+ end
53
+
54
+ # add single entry to the cache
55
+ # @param [Entry] entry to add. Could have contract not yet loaded but should be configured with attributes.
56
+ def add_to_cache(entry)
57
+ raise ArgumentError, "entry can't be nil" unless entry
58
+ @cache[entry.hash_id] = entry
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,76 @@
1
+ module Universa
2
+
3
+ # this is a base class for a contract stored in some contract chain. The implementation
4
+ # must inherit and implement its {#load} and {#save} methods at least. To do it,
5
+ # inherit and implement {ChainStore} to work with it.
6
+ #
7
+ # Notable features:
8
+ #
9
+ # - contract could be assigned only once, no matter how, so its fields could be cached.
10
+ # - origin, hash_id and parent are cached. So other contract parameters should be.
11
+ #
12
+ class StoredContract
13
+
14
+ # {ChainStore} instance to which it is connected
15
+ attr :chain_store
16
+ # {Contract} instance stored in it. Can be lazy-loaded
17
+ attr :contract
18
+ # {HashId} of the {#contract}
19
+ attr :hash_id
20
+
21
+ # @return [HashId] {#contract}.origin. See {Contract#origin}
22
+ def origin
23
+ @origin ||= @contract.origin
24
+ end
25
+
26
+ # @return [HashId] {#contract}.origin. See {Contract#parent}
27
+ def parent
28
+ @parent ||= @contract.parent
29
+ end
30
+
31
+ # Construct implementation connected to a given store
32
+ # @param [ChainStore] chain_store descendant class
33
+ def initialize(chain_store)
34
+ @chain_store = chain_store
35
+ @chain_store.is_a?(ChainStore) or raise ArgumentError, "ChainStore instance required"
36
+ @chain_store = chain_store
37
+ end
38
+
39
+ # For implementation logic, in particular, to make lazy loads.
40
+ # @return true if the stored contract is loaded into this instance
41
+ def has_contract?
42
+ !@contract.nil?
43
+ end
44
+
45
+ # Shortcut for `contract.packed`. See {Contract#packed}
46
+ # @return [String] binary string with contained contract packed transaction.
47
+ def packed_contract
48
+ @contract.packed
49
+ end
50
+
51
+ # override it to save the contract in the connected contract chain.
52
+ def save
53
+ raise NotFoundError
54
+ end
55
+
56
+ # override it to load the contract from the connected contract chain.
57
+ def load hash_id
58
+ raise NotFoundError
59
+ end
60
+
61
+ # Assign contract to the instance.
62
+ # @param [Contracy] new_contract to store
63
+ def contract=(new_contract)
64
+ raise IllegalStateError, "contract can't be reassigned" if has_contract?
65
+ @contract = new_contract
66
+ @hash_id = @contract.hash_id
67
+ @origin = @parent = nil
68
+ end
69
+
70
+ # Convenience method. Unoacks and stores the contract.
71
+ def packed_contract=(new_packed_contract)
72
+ self.contract = Contract.from_packed(new_packed_contract)
73
+ end
74
+
75
+ end
76
+ end
@@ -7,14 +7,16 @@ module Universa
7
7
  class SmartHash < Farcall::SmartHash
8
8
  end
9
9
 
10
- def retry_with_timeout(max_timeout = 15, max_times = 3, &block)
10
+ def retry_with_timeout(max_timeout = 25, max_times = 3, &block)
11
11
  attempt = 0
12
- Timeout::timeout(max_timeout, &block)
13
- rescue
14
- attempt += 1
15
- puts "timeout: retry (#$!): #{attempt}"
16
- retry if attempt < max_times
17
- raise
12
+ begin
13
+ Timeout::timeout(max_timeout, &block)
14
+ rescue
15
+ attempt += 1
16
+ puts "timeout: retry (#$!): #{attempt}"
17
+ retry if attempt < max_times
18
+ raise
19
+ end
18
20
  end
19
21
 
20
22
  module Parallel
@@ -26,7 +28,7 @@ module Universa
26
28
 
27
29
  @@pool = CachedThreadPool.new
28
30
 
29
- # Enumerates in parallel all items. Like {Enumerable#each_with_index}, but requires block.
31
+ # Enumerates in parallel all items. Like +Enumerable#each_with_index+, but requires block.
30
32
  # Blocks until all items are processed.
31
33
  #
32
34
  # @param [Proc] block to call with (object, index) parameters
@@ -55,7 +57,7 @@ module Universa
55
57
  each_with_index {|x, i| block.call(x)}
56
58
  end
57
59
 
58
- # Parallel version of the {Enumerable#map}. Creates a new array containing the values returned by the block,
60
+ # Parallel version of the +Enumerable#map+. Creates a new array containing the values returned by the block,
59
61
  # using parallel execution in threads.
60
62
  #
61
63
  # @return new array containing the values returned by the block.
@@ -1,4 +1,4 @@
1
1
  module Universa
2
2
  # Current gem version
3
- VERSION = "0.1.9"
3
+ VERSION = "0.2.1"
4
4
  end
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.9
4
+ version: 0.2.1
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-30 00:00:00.000000000 Z
11
+ date: 2018-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: farcall
@@ -154,11 +154,15 @@ files:
154
154
  - exe/universa
155
155
  - lib/universa.rb
156
156
  - lib/universa/binder.rb
157
+ - lib/universa/chain_store.rb
157
158
  - lib/universa/client.rb
158
159
  - lib/universa/contract.rb
159
160
  - lib/universa/errors.rb
161
+ - lib/universa/fs_store/entry.rb
162
+ - lib/universa/fs_store/file_store.rb
160
163
  - lib/universa/keys.rb
161
164
  - lib/universa/service.rb
165
+ - lib/universa/stored_contract.rb
162
166
  - lib/universa/string_utils.rb
163
167
  - lib/universa/tools.rb
164
168
  - lib/universa/umi.rb
@@ -185,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
189
  version: '0'
186
190
  requirements: []
187
191
  rubyforge_project:
188
- rubygems_version: 2.7.3
192
+ rubygems_version: 2.7.6
189
193
  signing_key:
190
194
  specification_version: 4
191
195
  summary: Expose Universa Java API to ruby