lib/universa/contract.rb in universa-0.1.9 vs lib/universa/contract.rb in universa-0.2.1

- old
+ new

@@ -32,15 +32,16 @@ def self.from_digest(digest_bytes) digest_bytes.force_encoding 'binary' invoke_static 'with_digest', digest_bytes end - # Construct from string representation of the ID, not to confuse with binary one. + # Construct from string representation of the ID, not to confuse with binary one. This method takes both + # regular base64 representation and RFC3548 url-safe modification, as from {#to_url_safe_string}. # # @param [String] string_id id string representation, like from +hash_id_instance.to_s+. See {#to_s}. def self.from_string(string_id) - string_id.force_encoding 'utf-8' + string_id.force_encoding('utf-8').gsub('-','+').gsub('_','/') invoke_static 'with_digest', string_id end # Get binary representation. It is shorter than string representation but contain non-printable characters and # can cause problems if treated like a string. Use {#to_s} to get string representation instead. @@ -55,30 +56,52 @@ # # @return [String] string representation def to_s Base64.encode64(get_digest).gsub(/\s/, '') end + + # Converts to URL-safe varianot of base64, as RFC 3548 suggests: + # the 63:nd / character with the underscore _ + # the 62:nd + character with the minus - + # + # Could be decoded safely back with {HashId.from_string} but not (most likely) with JAVA API itself + # @return [String] RFC3548 modified base64 + def to_url_safe_string + Base64.encode64(get_digest).gsub(/\s/, '').gsub('/','_').gsub('+', '-') + end + + # To use it as a hash key_address. + # @return hash calculated over the digest bytes + def hash + bytes.hash + end + + # To use it as a hash key_address. Same as this == other. + def eql? other + self == other + end + end # Universa contract adapter. class Contract < RemoteAdapter remote_class "com.icodici.universa.contract.Contract" # Create simple contract with preset critical parts: # # - expiration set to 90 days unless specified else - # - issuer role is set to the address of the issuer key, short ot long + # - issuer role is set to the address of the issuer key_address, short ot long # - creator role is set as link to issuer # - owner role is set as link to issuer # - change owner permission is set to link to owner # - # The while contract is then signed by the issuer key. Not that it will not seal it: caller almost always + # The while contract is then signed by the issuer key_address. Not that it will not seal it: caller almost always # will add more data before it, then must call #seal(). # # @param [PrivateKey] issuer_key also will be used to sign it # @param [Time] expires_at defaults to 90 days - # @param [Boolean] use_short_address set to true to use short address of the issuer key in the role + # @param [Boolean] use_short_address set to true to use short address of the issuer key_address in the role # @return [Contract] simple contact, not sealed def self.create issuer_key, expires_at: (Time.now + 90 * 24 * 60 * 60), use_short_address: false contract = Contract.new contract.set_expires_at expires_at contract.set_issuer_keys(use_short_address ? issuer_key.short_address : issuer_key.long_address) @@ -90,10 +113,11 @@ contract end # Load from transaction pack def self.from_packed packed + packed.nil? and raise ArgumentError, "packed contract required" packed.force_encoding 'binary' self.invoke_static "fromPackedTransaction", packed end # seal the contract @@ -122,38 +146,54 @@ # @return [Role] owner role def owner get_owner end - def owner= key - set_owner_key key + # Set owner to the key_address, usable only in the simplest case where owner is the single address. + # @param [KeyAddress | PublicKey] key_address + def owner=(key_address) + set_owner_key key_address end # Shortcut for is_ok def ok? is_ok end # shortcut for getHashId + # @return [HashId] of the contracr def hash_id - get_id + getId() end - # shortcut for get_expires_at + # @return [HashId] of the origin contract + def origin + getOrigin() + end + + # @return [HashId] pf the parent contracr + def parent + getParent() + end + + # shortcut for get_expires_at. Get the contract expiration time. def expires_at get_expires_at end - def expires_at= time + # set +expires_at+ field + # @param [Time] time when this contract will be expired, if yet +APPROVED+. + def expires_at=(time) set_expires_at time end # @return definition data def definition @definition ||= get_definition.get_data end + # Return +state+ binder. Shortcut for Java API +getStateData()+ def state @state ||= getStateData() end # Get +transactional.data+ section creating it if need @@ -173,11 +213,11 @@ end # Write helper for many token-like contracts containing state.data.amount. Saves value # in state.data.anomount and properly encodes it so it will be preserved on packing. # - # @param [Object] value, should be some representation of a number (also string) + # @param [Object] value should be some representation of a number (also string) def amount= (value) state[:amount] = value.to_s.force_encoding('utf-8') end # Get packed transaction containing the serialized signed contract and all its counterparts. @@ -201,10 +241,14 @@ # @return [String] possibly empty '' def errors_string getErrors.map {|e| "(#{e.object || ''}): #{e.error}"}.join(', ').strip end - def can_perform_role name, *keys + # Test that some set of keys could be used to perform some role. + # + # @param [String] name of the role to check + # @param [PublicKey] keys instances to check against + def can_perform_role(name, *keys) getRole(name.to_s).isAllowedForKeys(Set.new keys.map {|x| x.is_a?(PrivateKey) ? x.public_key : x }) end \ No newline at end of file