UMI protocol (beta)


Download the UMI tool from our cloud storage:  lnd.im/umi

UMI protocol (meaning “Universa Method Invocation”) is used to create Universa API bindings for various programming languages, in an RMI paradigm.

Universa UMI is executed over farcall protocol calls (https://github.com/sergeych/farcall/wiki) in JSON format.

See also UMI usage code samples in other languages.

Running UMI tool

Extract the contents of the archive and start it in any of 5 available modes. Note that JSON is always used to transmit farcall packets.

Pipe mode

Execute ./bin/umi and use process stdin/stdout

socket mode

This is not recommended mode as the socket connection is relatively hard to protect from unintended connections.

./bin/umi --listen tcp://localhost:$PORT.

Connect to the socket and start farcal over the connection.

Unix socket mode

Run ./bin/umi --listen unix://$path_to_unix_socket

Connect to the socket and start farcal over the connection.

Commands list

API Conventions

The API functions are specified in the following form:

some_method({foo: <string>,bar: <timestamp>}) -> {result: <string>}

Which means:

  • the callable method some_method (with websock unichat endpoint also available as someMethod)
  • it takes 2 named arguments, foo of type string and bar of type timestamp which all are explained in detail later
  • it returns single named value, result, which is a string.

UMI API data types

The api is multiplatform and so are its data types.

name type
<int> integer signed value of 32 bits
<long> integer signed value of 64 bits
<string> a string
<boolean> a boolean
<decimal_string> decimal value (like float number but of higher precision)
<timestamp> date and time in ISO 8601: Extended Format
<simple_type> any on above listed types
<remote_object> a structure represending remote object created on UMI tool side { __type: RemoteObject, id: <long>, className: <string> }
<remote_object_no_type> a structure represending remote object created on UMI tool side { __type: RemoteObject, id: <long>, className: <opt_string> }
<serialized_object> a structure represending complex datatype in the serialized form { __type: <string>, ... }
<invoсation_result> is either <simple_type> or <serialized_object> or <remote_object>
<invoсation_param> is either <simple_type> or <serialized_object> or <remote_object_no_type>

If the type is prepended with opt_ prefix, e.g. <opt_string>, it means that corresponding parameter may be omitted or missing.

If the type is prepended with vararg_ prefix, e.g. <vararg_string>, it means that there could be 0 or more parameters of corresponding type

instantiate

instantiate({objectType: <string>, args: <vararg_invocation_param>}) 
-> {result: <remote_object>}

Creates a remote object of given type. objectType must be on the list of instantiable types.

invoke

invoke({remoteObjectId: <long>, methodName: <string>, args: <vararg_invocation_param>}) 
-> {result: <invocation_result>}

Invokes given method of remote object with given remoteObjectId.

invoke({objectType: <string>, methodName: <string>, args: <vararg_invocation_param>}) 
-> {result: <invocation_result>}

Invokes given static method of given type. objectType must be on the list of instantiable types.

The actual result type is depending on return value of method being invoked:

  • <simple_type> for methods returning simple types
  • <remote_object> for methods returning instance of linkable types
  • <serialized_object> for methods returning type that serialization is possible for
  • <string> for other methods. Some kind of string representaion of the returned object (java.lang.Object.toString())

get

get({remoteObjectId: <long>}) -> {result: <serialized_object>}

Returns serialized version of remote object. It takes object id as parameter.

drop_objects

drop_objects({ids: [<long>,...]}) -> {result: <int>}

Destroys given remote objects. It takes ids as the paramenters. The number of remote objects left is returned

drop_all

drop_all() -> {result: <int>}

Destroys all remote objects. The number of remote objects left is returned (zero)

version

version() -> {protocol: <string>, system: <string>, version: <string> }

Examples

Create key address from string

Remote object of type KeyAddress is created first

Scala:

val jsc: JsonConnector = new JsonConnector(socket.getInputStream, socket.getOutputStream)
val ofc = new ObjectFarcall(jsc)
ofc.start()
val keyAddress1 = ofc.instantiate("KeyAddress", "YnFjR2pcTK3rFko1z7dP9qnyBRvuAwK9VBpfeR2m6sBgyQhbtb")

Farcall:

command: `instantiate`
params:  ["KeyAddress","YnFjR2pcTK3rFko1z7dP9qnyBRvuAwK9VBpfeR2m6sBgyQhbtb"]
result: {"className":"com.icodici.crypto.KeyAddress","__type":"RemoteObject","id":1}

Raw exchange:

{"serial":0,"args":["KeyAddress","YnFjR2pcTK3rFko1z7dP9qnyBRvuAwK9VBpfeR2m6sBgyQhbtb"],"cmd":"instantiate"}

{"result":{"className":"com.icodici.crypto.KeyAddress","__type":"RemoteObject","id":1},"ref":0,"serial":0}

Generate private key

Private key of 2048 bits strength is generated then

Scala:

val privateKey1 = ofc.instantiate("PrivateKey", 2048)

Farcall:

command: `instantiate`
params:  ["PrivateKey",2048]
result: {"className":"com.icodici.crypto.PrivateKey","__type":"RemoteObject","id":2}

Raw exchange:

{"serial":1,"args":["PrivateKey",2048],"cmd":"instantiate"}

{"result":{"className":"com.icodici.crypto.PrivateKey","__type":"RemoteObject","id":2},"ref":1,"serial":1}

Create another key address from string

Scala:

val keyAddress2 = ofc.instantiate("KeyAddress", "Jg8mgpELueFVbtVTbhLZMT3ZzyZSDftgzXhExshjDc5j5EasTjrHd2A9qDyXHJQJj3726Z7t")

Farcall:

command: `instantiate`
params:  ["KeyAddress","Jg8mgpELueFVbtVTbhLZMT3ZzyZSDftgzXhExshjDc5j5EasTjrHd2A9qDyXHJQJj3726Z7t"]
result: {"className":"com.icodici.crypto.KeyAddress","__type":"RemoteObject","id":3}

Raw exchange:

{"serial":2,"args":["KeyAddress","Jg8mgpELueFVbtVTbhLZMT3ZzyZSDftgzXhExshjDc5j5EasTjrHd2A9qDyXHJQJj3726Z7t"],"cmd":"instantiate"}

{"result":{"className":"com.icodici.crypto.KeyAddress","__type":"RemoteObject","id":3},"ref":2,"serial":2}

Compare two addresses

Invoke isMatchingKeyAddress method of one remote object passing other remote object as parameter

Scala:

val res =ofc.invoke(keyAddress1,"isMatchingKeyAddress",keyAddress2)
assert(res == false)

Command:

command: `invoke`
params:  [1,"isMatchingKeyAddress",{"__type":"RemoteObject","id":3}]
result: false

Raw exchange:

{"serial":3,"args":[1,"isMatchingKeyAddress",{"__type":"RemoteObject","className":"com.icodici.crypto.KeyAddress","id":3}],"cmd":"invoke"}

{"result":false,"ref":3,"serial":3}

Get 2nd address (serialized)

Scala:

val address2value = ofc.get(keyAddress2)

Farcall:

command: `get`
params:  [3]
result: {"uaddress":{"base64":"EK66czfW9p4Tjt4ObQT1eH9DZ7LKU8sEwohkMV3ATC2gDYBMkyBl0vOxAkDw8B3I5Y6kqCc=","__type":"binary"},"__type":"KeyAddress"}

Raw exchange:

{"serial":4,"args":[3],"cmd":"get"}

{"result":{"uaddress":{"base64":"EK66czfW9p4Tjt4ObQT1eH9DZ7LKU8sEwohkMV3ATC2gDYBMkyBl0vOxAkDw8B3I5Y6kqCc=","__type":"binary"},"__type":"KeyAddress"},"ref":4,"serial":4}

Create Contract object

Scala:

val contract = ofc.instantiate("Contract",privateKey1)

Farcall:

command: `instantiate`
params:  ["Contract",{"__type":"RemoteObject","id":2}]
result: {"className":"com.icodici.universa.contract.Contract","__type":"RemoteObject","id":4}

Raw exchange:

{"serial":5,"args":["Contract",{"__type":"RemoteObject","className":"com.icodici.crypto.PrivateKey","id":2}],"cmd":"instantiate"}

{"result":{"className":"com.icodici.universa.contract.Contract","__type":"RemoteObject","id":4},"ref":5,"serial":5}

Set issuer of a Contract

Two key addresses and private key are passed to setIssuerKeys method. Second address is passed as a value

Scala:

ofc.invoke(contract,"setIssuerKeys",keyAddress1,address2value,privateKey1)

Farcall:

command: `invoke`
params:  [4,"setIssuerKeys",{"__type":"RemoteObject","id":1},{"uaddress":{"base64":"EK66czfW9p4Tjt4ObQT1eH9DZ7LKU8sEwohkMV3ATC2gDYBMkyBl0vOxAkDw8B3I5Y6kqCc=","__type":"binary"},"__type":"KeyAddress"},{"__type":"RemoteObject","id":2}]
result: {"className":"com.icodici.universa.contract.roles.SimpleRole","__type":"RemoteObject","id":5}

Raw exchange:

{"serial":6,"args":[4,"setIssuerKeys",{"__type":"RemoteObject","className":"com.icodici.crypto.KeyAddress","id":1},{"__type":"KeyAddress","uaddress":{"__type":"binary","base64":"EK66czfW9p4Tjt4ObQT1eH9DZ7LKU8sEwohkMV3ATC2gDYBMkyBl0vOxAkDw8B3I5Y6kqCc="}},{"__type":"RemoteObject","className":"com.icodici.crypto.PrivateKey","id":2}],"cmd":"invoke"}

{"result":{"className":"com.icodici.universa.contract.roles.SimpleRole","__type":"RemoteObject","id":5},"ref":6,"serial":6}

Seal contract and get sealed binary

Scala:

val bytes = ofc.invoke(contract,"seal")

Farcall:

command: `invoke`
params:  [4,"seal"]
result: {"base64":"JyNkYXRhxC4D....p7SCiw==","__type":"binary"}

Raw exchange:

{"serial":7,"args":[4,"seal"],"cmd":"invoke"}

{"result":{"base64":"JyNkYXRhxC4D....p7SCiw==","__type":"binary"},"ref":7,"serial":7}

Tips

Passing enum to a function

Enums can be easily passed by name. Please note that current implementation does not differ enums arguments from string arguments when matching method that about to invoked.

    val lr = ofc.instantiate("ListRole","somerole")
    ofc.invoke(lr,"setMode","ANY")
{"serial":0,"args":["ListRole"],"cmd":"instantiate"}
{"result":{"className":"com.icodici.universa.contract.roles.ListRole","__type":"RemoteObject","id":1},"ref":0,"serial":0}
{"serial":1,"args":[1,"setMode","ANY"],"cmd":"invoke"}
{"result":null,"ref":1,"serial":1}

Passing array to a function

Arrays are passed as normal JSArrays in arguments

    val conditionsBinder = ofc.invoke("Binder","of","all_of",Seq("ref.state.origin==this.id","ref.state.revision==1"))
    val reference = ofc.instantiate("Reference")
    ofc.invoke(reference,"setConditions",conditionsBinder)
{"serial":0,"args":["Binder","of","all_of",["ref.state.origin==this.id","ref.state.revision==1"]],"cmd":"invoke"}
{"result":{"className":"net.sergeych.tools.Binder","__type":"RemoteObject","id":1},"ref":0,"serial":0}
{"serial":1,"args":["Reference"],"cmd":"instantiate"}
{"result":{"className":"com.icodici.universa.contract.Reference","__type":"RemoteObject","id":2},"ref":1,"serial":1}
{"serial":2,"args":[2,"setConditions",{"__type":"RemoteObject","className":"net.sergeych.tools.Binder","id":1}],"cmd":"invoke"}
{"result":{"className":"com.icodici.universa.contract.Reference","__type":"RemoteObject","id":3},"ref":2,"serial":2}

Passing set to a function

Set needs to be instantiated on host and passed as RemoteObject argument. Set can be initialized with elements of array passed as a parameter to instantiate command. Elements can be added later with add/addAll methods getting single argument or array as parameter.

   val contract =ofc.instantiate("Contract")
    ofc.invoke(contract,"seal")

    val privateKey1 = ofc.instantiate("PrivateKey", 2048)
    val privateKey2 = ofc.instantiate("PrivateKey", 2048)
    val privateKey3 = ofc.instantiate("PrivateKey", 2048)
    val privateKey4 = ofc.instantiate("PrivateKey", 2048)
    val privateKey5 = ofc.instantiate("PrivateKey", 2048)

    //instantiate set. initialize with privateKey1 and privateKey2
    val set = ofc.instantiate("Set", Seq(privateKey1,privateKey2))

    //Add privateKey3 to set
    ofc.invoke(set,"add",privateKey3)

    //Add two more keys (privateKey4, privateKey5) from array
    ofc.invoke(set,"addAll",Seq(privateKey4,privateKey5))
    ofc.invoke(contract,"addSignatureToSeal",set)
{"serial":0,"args":["Contract"],"cmd":"instantiate"}
{"result":{"className":"com.icodici.universa.contract.Contract","__type":"RemoteObject","id":1},"ref":0,"serial":0}
{"serial":1,"args":[1,"seal"],"cmd":"invoke"}
{"result":{"__type":"binary", ... },"ref":1,"serial":1}
{"serial":2,"args":["PrivateKey",2048],"cmd":"instantiate"}
{"result":{"className":"com.icodici.crypto.PrivateKey","__type":"RemoteObject","id":3},"ref":2,"serial":2}
{"serial":3,"args":["PrivateKey",2048],"cmd":"instantiate"}
{"result":{"className":"com.icodici.crypto.PrivateKey","__type":"RemoteObject","id":4},"ref":3,"serial":3}
{"serial":4,"args":["PrivateKey",2048],"cmd":"instantiate"}
{"result":{"className":"com.icodici.crypto.PrivateKey","__type":"RemoteObject","id":5},"ref":4,"serial":4}
{"serial":5,"args":["PrivateKey",2048],"cmd":"instantiate"}
{"result":{"className":"com.icodici.crypto.PrivateKey","__type":"RemoteObject","id":6},"ref":5,"serial":5}
{"serial":6,"args":["PrivateKey",2048],"cmd":"instantiate"}
{"result":{"className":"com.icodici.crypto.PrivateKey","__type":"RemoteObject","id":7},"ref":6,"serial":6}
{"serial":7,"args":["Set",[{"__type":"RemoteObject","className":"com.icodici.crypto.PrivateKey","id":3},{"__type":"RemoteObject","className":"com.icodici.crypto.PrivateKey","id":4}]],"cmd":"instantiate"}
{"result":{"className":"java.util.HashSet","__type":"RemoteObject","id":8},"ref":7,"serial":7}
{"serial":8,"args":[8,"add",{"__type":"RemoteObject","className":"com.icodici.crypto.PrivateKey","id":5}],"cmd":"invoke"}
{"result":true,"ref":8,"serial":8}
{"serial":9,"args":[8,"addAll",[{"__type":"RemoteObject","className":"com.icodici.crypto.PrivateKey","id":6},{"__type":"RemoteObject","className":"com.icodici.crypto.PrivateKey","id":7}]],"cmd":"invoke"}
{"result":true,"ref":9,"serial":9}
{"serial":10,"args":[1,"addSignatureToSeal",{"__type":"RemoteObject","className":"java.util.HashSet","id":8}],"cmd":"invoke"}
{"result":{"base64":"JyNkYXR...qpNhw==","__type":"binary"},"ref":11,"serial":11}

Getting Object[] elements

For types like Object[] there are only 2 invokable methods: [] and length. First takes an integer as the only argument, second takes no args.

Currently the only linkable []-type is Contract[] but it can be changed in future.

    val key = ofc.instantiate("PrivateKey",2048)
    val contract = ofc.instantiate("Contract",key)
    ofc.invoke(contract,"seal")
    val contracts = ofc.invoke(contract,"split",3).asInstanceOf[ObjectFarcall.RemoteObject]
    val size = ofc.invoke(contracts,"length")
    val lastPart = ofc.invoke(contracts,"[]",2).asInstanceOf[ObjectFarcall.RemoteObject]
    assert(size == 3)
    assert(lastPart.className == classOf[Contract].getCanonicalName)
{"serial":0,"args":["PrivateKey",2048],"cmd":"instantiate"}
{"result":{"className":"com.icodici.crypto.PrivateKey","__type":"RemoteObject","id":1},"ref":0,"serial":0}
{"serial":1,"args":["Contract",{"__type":"RemoteObject","className":"com.icodici.crypto.PrivateKey","id":1}],"cmd":"instantiate"}
{"result":{"className":"com.icodici.universa.contract.Contract","__type":"RemoteObject","id":2},"ref":1,"serial":1}
{"serial":2,"args":[2,"seal"],"cmd":"invoke"}
{"result":{"base64":"JyNk...4t4w==","__type":"binary"},"ref":2,"serial":2}
{"serial":3,"args":[2,"split",3],"cmd":"invoke"}
{"result":{"className":"com.icodici.universa.contract.Contract[]","__type":"RemoteObject","id":4},"ref":3,"serial":3}
{"serial":4,"args":[4,"length"],"cmd":"invoke"}
{"result":3,"ref":4,"serial":4}
{"serial":5,"args":[4,"[]",2],"cmd":"invoke"}
{"result":{"className":"com.icodici.universa.contract.Contract","__type":"RemoteObject","id":5},"ref":5,"serial":5}