General Java API


Topic: Java API

See other Java API features at Software Developer Central: Java API at whole, Contracts Service, Distributed Storage, etc.

To start using Java API, you can add com.icodici:universa_core:3.8.2 dependency from Universa public Maven repository. See more details at Maven repository page.

This feature is available in package com.icodici.universa.node2.network, as the Client class; see it on Github.

Overview

Java API is a set of premade classes, procedures, structures and constants provided by Universa for use in external software products. Use the Java API to develop all kinds of applications and services.

Consult the  Javadoc for details.

Key

Create new key pair

Create RSA Private key with given size (2048 bits, 4096 bits).

Use the code:

PrivateKey privateKey = new PrivateKey(2048);

You can get the private and public key from a binary file containing the key.

Use the code:

PrivateKey privateKey = new PrivateKey(Do.read(ROOT_PATH + "mykey.private.unikey"));
PublicKey publicKey = new PublicKey(Do.read(ROOT_PATH + "mykey.public.unikey"));

Method returns valid public key:

AbstractKey getPublicKey()

Use the code:

PrivateKey privateKey = new PrivateKey(2048);
PublicKey publicKey = privateKey.getPublicKey();

Key address

The Key address is a short (51 or 72 symbols) representation of any key. To generate short or long address for the key, you need to use one of the following methods:

KeyAddress getShortAddress()

KeyAddress getLongAddress()

Use the code:

KeyAddress shortAddress = publicKey.getShortAddress();
KeyAddress longAddress = publicKey.getLongAddress();

Matching of keys and addresses

Check that the key matches this address, i.e. it has the same type and the digest of its components is the same.

boolean isMatchingKey(AbstractKey key)

  • key to check
  • return true if the key matches (that means, it is the key corresponding to this address)

Use the code:

boolean result = keyAddress.isMatchingKey(publicKey);

Method that could be used to check that the address matches key information:

boolean isMatchingKeyAddress(KeyAddress other)

It is permissible to compare only addresses of the same length.

Use the code:

boolean result = keyAddress.isMatchingKeyAddress(matchingKeyAddress);

Contract

If necessary, you can use the Contracts Service, which is a service that helps you create different types of contracts, perform and perform various common operations with them. And also to create a specific contract, you can use contract constructors.

Create new contract

Create an empty new contract or create a default new contract using a provided key as issuer and owner and sealer. This constructor adds key as sealing signature so it is ready to seal just after construction, but it is necessary to put him real data. It is allowed to change owner, expiration and data fields after creation (but before sealing). Change owner permission is added by default:

Contract()

Contract(PrivateKey key)

  • key is private Key for creating roles "issuer", "owner", "creator" and sign contract.

Default expiration is set to 90 days.


Extract contract from v2 or v3 sealed form, getting revoking and new items from sealed unicapsule and referenced items from the transaction pack supplied:

Contract(byte[] sealed, @NonNull TransactionPack pack)

  • sealed binary sealed contract;
  • pack the transaction pack to resolve dependeincise agains.

Extract contract from v2 or v3 sealed form, getting revoking and new items from sealed unicapsule:

Contract(byte[] data)

  • data binary sealed contract.

Extract old, deprecated v2 self-contained binary partially unpacked by the TransactionPack, and fill the transaction pack with its contents. This contsructor also fills transaction pack instance with the new and revoking items included in its body in v2:

Contract(byte[] sealed, Binder data, TransactionPack pack)

  • sealed binary sealed contract;
  • data unpacked sealed data (it is ready by the time of calling it);
  • pack is TransactionPack for contract was sent with.

Use the code:

Contract contract = new Contract(privateKey);

The DSL templates are useful to create new contracts. There are some DSL templates in the Github to play with. If you have DSL template stored in the .yml file, e.g. template.yaml, you can create new contract by using the method:

Contract fromDslFile(String fileName)

  • fileName is path to file containing Yaml representation of contract.

No signatures are added automatically. It is required to add signatures before check.

Use the code:

Contract baseContract = Contract.fromDslFile(FILE_PATH + "template.yml");

Edit contract

Сhange fields in the contract

When creating a contract, you can set the fields you need in the contract.

Methods get Definition, State and Transactional section of a contract:

Definition getDefinition()

State getState()

Transactional getTransactional()

Using the method getState(), you can get data from sections:

Use the code:

contract.getState().getData().get("amount")

Method get data section of contract state:

Binder getStateData()

You can add Binder as a field with the specified index and return it in a typed manner:

<T extends Object> T set(String key, T object)

Use the code:

contract.getStateData().set("field_name", field_value);   
contract.getDefinition().getData().set("type", "chief accountant assignment");    

Add new items

Method add one or more siblings to the contract. Note that those must be sealed before calling seal() or getPackedTransaction(). Do not re-seal siblings as this changes the id.

void addNewItems(Contract... newContracts)

  • newContracts is comma-separated contracts

Use the code:

Contract singleContract = new Contract(myKey);
Contract contract = new Contract(myKey);
contract.seal();
singleContract.addNewItems(contract);

Sign the contract

Universa requires each new contract or contract revision to be properly signed by the required parties.

Methods add private key or collection of private keys to keys contract binary to be signed with when sealed next time. It is called before seal():

void addSignerKey(PrivateKey privateKey)

void addSignerKeys( keys)

  • privateKey private key;
  • keys private keys.

The improperly signed contracts will not be accepted by the Universa network.

Use the code:

contract.addSignerKey(privateKey);

Methods add private key from file to keys contract binary to be signed with when sealed next time. It is called before seal():

void addSignerKeyFromFile(String fileName)

  • fileName path to file containing private key.

Use the code:

contract.addSignerKeyFromFile(FILE_PATH+"mykey.private.unikey");

If you got contracts from third-party (another participant) and need to sign it for example contracts that shoul be sign with two persons you need to use methods:

void addSignatureToSeal(PrivateKey privateKey)

void addSignatureToSeal(Set privateKeys)

void addSignatureToSeal(byte[] signature, PublicKey publicKey)

  • privateKey private key to sign contract;
  • privateKeys private keys to sign contract.
  • signature prepared singature (see ExtendedSignature).
  • publicKey public key that corresponds to signature.

Methods add signature to sealed (before) contract. Methods do not deserialize or change the bytes of the contract, but will change sealed and hashId.

Use the code:

contract.addSignatureToSeal(privateKeys); 

If you need remove all signatures from sealed binary use method:

void removeAllSignatures()

Use the code:

contract.removeAllSignatures();

Seal the contract

Seal contract to binary. This methods adds signatures and returns contract's sealed unicapsule:

byte[] seal()

  • returns contract's sealed unicapsule.

Use the code:

contract.addSignerKey(privateKeys);
contract.seal();

Check contract

It is advised to check the contract state locally before sending it to the network. This includes checking contract state modification, checking new items, revoke permissions and references acceptance. Errors found can be accessed with getErrors For it, you can use the following methods:

boolean check(String prefix) boolean check()

  • prefix is included in errors text. Used to differ errors found in contract from errors of subcontracts (revoking, new);
  • throws is Quantiser.QuantiserException when quantas limit was reached during check;
  • returns true if this instance is completely checked with positive result.

Method check the the document is valid assuming all mentioned items are OK, e.g. items, approved by it (if any), items revoking by it and referenced by it.

You can get errors by using the method:

void traceErrors()

Use the code:

contract.addSignerKey(privateKeys);
contract.seal();
if (!contract.check()) traceErrors();

Method check contract to be a valid "U" payment. This includes standard contract check and additional payment related checks

boolean paymentCheck(Set issuerKeys)

  • issuerKeys addresses of keys used by the network to issue "U";
  • returns true if this instance is completely checked with positive result.

Register contract

You can create a paid transaction, which consist of the contract you want to register, and and the payment contract that will be spend to process transaction and take the fees. For this you need to use the Contracts Service.

You can estimate the cost for contract processing:

int getProcessedCostU()

  • returns cost in "U".

The method to register the packed binary contract and wait for the consensus:

boolean registerParcel(byte[] packedParcel, long millisToWait)

  • packedParcel packed parcel;
  • millisToWait wait for the consensus as long as specified time, <= 0 means no wait (returns some pending state from registering);
  • returns last item status returned by the network.

Use the code:

if (contract.check()) {
    int amount = contract.getProcessedCostU();
    Parcel parcel = ContractsService.createParcel(contract, paymentContract, amount, keys, false);
    ItemResult itemResult = client.registerParcel(parcel.pack(), 5000);
} else {
    contract.traceErrors(); 
}

Revocation of contracts

You can revoke some contracts you own (or has revocation permission for). For this you need to use the Contracts Service:

Use the code:

Contract revokeContract = ContractsService.createRevocation(sourceContract, privateKey);

Upon the successful revokeContract registration, the source contract will be revoked.


Method add one or more contracts to revoke. The contracts must be approved loaded from a binary. Do not call seal() on them as resealing discards network approval by changing the id:

addRevokingItems(Contract... toRevoke)

  • toRevoke is comma-separated contract to revoke.

Use the code:

contract revokingContract = new Contract(privateKeys);
revokingContract.addRevokingItems(contract);

Create new revision

Method create new revision to be changed, signed, sealed and then ready to approve. Created "revision" contract is a copy of this contract, with all fields and references correctly set:

Contract createRevision()

Contract createRevision(Contract.Transactional transactional, PrivateKey... keys)

Contract createRevision(java.util.Collection keys)

Contract createRevision(Collection keys)

After this call one need to change mutable fields, add signing keys, seal it and then apss to Universa network for approval.

  • transactional is section to create new revision with new revision of this contract, identical to this one, to be modified;
  • returns new revision of this contract, identical to this one, to be modified.

Use the code:

Contract baseContract = Contract.fromDslFile(ROOT_PATH + "template.yml");  
newBaseContract = baseContract.createRevision(keys);

Transactional section

When creating a root contract or a new revision, section "transactional" may be added to the contract. Transactional section is processed once during network check and discarded. Section is involved in the checking of the contract, take into account its roles and permissions, its references are checked. When checking contract changes, changes in the transactional section are ignored. When creating a new revision, the existing transactional section is not copied, the old one the content is deleted.

Transactional section can contain the following fields:

field value
id If this field is present, it must contain a string identifier, unique within the transaction pack.
valid_until Validity period of transactional section. If this field is present, the contract revision can`t be accepted by the system later than specified time. This field does not change the duration of the contract, it determines only the deadline in which he can be submitted for registration. Do not register contracts or revisions that expire in the next few minutes. The recommended minimum period is 5 minutes.
references Transactional references(link) checked once at the registration of the contract or revision.
data Any additional data that you want to include in transactional section.

The following are the Transactional class methods to set and get specified fields.

Methods set transactional references:

void addReference(Reference reference)

void removeReference(Reference reference)

Method get transactional references:

List getReferences()

Method set transactional id:

void setId(String id)

Method get transactional id:

String getId()

Method set a validity period of transactional section:

void setValidUntil(Long val)

Method get a validity period of transactional section:

Long getValidUntil()

Method get transactional data:

Binder getData()

Contract class method of creation transactional section:

Contract.Transactional createTransactionalSection()

Example of creation a transactional section:

//Create new contract revision
Contract newRevision = contract.createRevision();
//Create transactional section
Contract.Transactional newTransactional = newRevision.createTransactionalSection();
newTransactional.setId(HashId.createRandom().toBase64String());
//Add transactional reference
Reference ref = new Reference();
ref.transactional_id = newTransactional.getId();
ref.type = Reference.TYPE_TRANSACTIONAL;
ref.required = true;
ref.signed_by = new ArrayList<>();
ref.signed_by.add(key);
newTransactional.addReference(ref);
newRevision.seal();

ExtendedSignature

Extended signature is the data being added to contract sealed binary. It can be created in two possible ways.

Passing private key itself and the data that signature is created for (contract bytes).

static public byte[] sign(PrivateKey key, byte[] data)

  • key is PrivateKey to sign with
  • data to be sign with key
  • returns prepared signature

In three steps:

  1. Create target signature using ExtendedSignature.createTargetSignature method.
  2. Manually sign target signature with SHA512 and SHA3(384bit)
  3. Create signature ExtendedSignature.of method using target signature: raw, signed with SHA512 and signed with SHA3(384bit)

static public byte[] createTargetSignature(PublicKey publicKey, byte[] data, boolean savePublicKey)

  • publicKey is public key corresponding the key to sign with
  • data binary data signature is created for
  • savePublicKey indicates if public key binary should be included into signed binary
  • returns target signature

static public byte[] of(byte[] targetSignature, byte[] sign, byte[] sign2)

  • targetSignature is public key corresponding the key to sign with
  • sign target signature signed with SHA512
  • sign2 target signature signed with SHA3(384bit)
  • returns prepared signature

Roles

Simple Role

Simple Role is the base class for any role combination, e.g. single key, any key from a set, all keys from a set, minimum number of key from a set and so on. The role class provides the ability to create and use a basic role in contracts. Simple Role express "all of" logic, e.g. if all of the presented keys are listed, then the role is allowed.

To create a Simple Role, you need to use the Simple Role constructors.

Create new SimpleRole and add one KeyRecord associated with role:

SimpleRole(String name, @NonNull KeyRecord keyRecord)

  • name is name of role;
  • keyRecord is KeyRecord associated with role.

Create new empty SimpleRole. Keys or records to role may be added with addKeyRecord(KeyRecord):

SimpleRole(String name)

  • name is name of role.

Create new SimpleRole. Records are initialized from the collection and can have the following types: PublicKey, PrivateKey, KeyRecord, KeyAddress, AnonymousId:

SimpleRole(String name, @NonNull Collection records)

  • name is name of role;
  • records is collection of records to initialize role.

Use the code:

SimpleRole ownerRole = new SimpleRole("owner");

Methods add KeyRecord to role:

void addKeyRecord(KeyRecord keyRecord)

  • keyRecord is KeyRecord;

Use the code:

SimpleRole ownerRole = new SimpleRole("owner");
for (PublicKey k : ownerKeys) {
    KeyRecord kr = new KeyRecord(k);
    ownerRole.addKeyRecord(kr);
}

List Role

List Role is the class for combining other roles (sub-roles) in the "and", "or" and "any N of" principle.

Mode of combining roles:

  • ALL - role could be performed only if set of keys could play all sub-roles;
  • ANY - role could be performed if set of keys could play any role of the list;
  • QUORUM - role could be played if set of keys could play any quorrum set of roles, e.g. at least any N subroles, controlled by the setQuorum(int) method.

To create a List Role, you need to use the List Role constructors.


Create empty role combining other roles (sub-roles). To be initialized from dsl later:

ListRole() ListRole(String name)

  • name is role name.

Create new role combining other roles (sub-roles):

ListRole(String name, Mode mode, @NonNull Collection roles)

  • name is role name;
  • mode is mode of sub-roles combining: "and", "or" and "any N of" principle;
  • roles is collection of sub-roles.

Create new role combining other roles (sub-roles) in the "any N of" principle mode QUORUM:

ListRole(String name, int quorumSize, @NonNull Collection roles)

  • name is role name;
  • quorumSize is N in "any N of" principle;
  • roles is collection of sub-roles.

Use the code:

Contract contract = new Contract();
SimpleRole ownerRole1 = new SimpleRole("owner1");
SimpleRole ownerRole2 = new SimpleRole("owner2");
ownerRole1.addKeyRecord(new aKeyRecord(PublicKey()));
ownerRole2.addKeyRecord(new bKeyRecord(PublicKey()));
contract.registerRole(ownerRole1);
contract.registerRole(ownerRole2);
ListRole listRole = new ListRole("list_role", ListRole.Mode.ANY, Do.listOf(ownerRole1, ownerRole2));

You can set mode to either mode ALL or mode ANY, QUORUM mode could be set only with setQuorum(int) call:

void setMode(Mode newMode)

  • newMode mode to set.

Method adds sub-role to combining role:

ListRole addRole(Role role)

  • role is sub-role.

Use the code:

SimpleRole ownerRole = new SimpleRole("owner");
SimpleRole issuerRole = new SimpleRole("issuer");
ListRole listRole = new ListRole("list_role");
listRole.setMode(ListRole.Mode.ALL);
listRole.addRole(ownerRole);
listRole.addRole(issuerRole);

You can create a link to a named role. Note that such links can be created ahead of time, e.g. when there is no bound contract or the target role does not yet exist. Just be sure to bind the contract with setContract(Contract) before using the instance:

RoleLink(String name, String roleName)

  • name is new role name;
  • roleNameis existing role name.

Use the code:

RoleLink ownerLink = new RoleLink("@owner_link","owner");
ownerLink.setContract(contract);

Add Role to the contract

Method resolve object describing role and create new role object or symlink to named role instance, ensure it is register and return it. If it is a Map, tries to construct and register Role then return it:

Role createRole(String roleName, Object roleObject)

  • roleName is name of the role;
  • roleObject is object for role creating;
  • returns created Role.

Method register new role. Name must be unique otherwise existing role will be overwritten:

Role registerRole(Role role)

  • param role;
  • returns registered role.

Use the code:

SimpleRole ownerRole = new SimpleRole("owner");
contract.createRole("owner", ownerRole);
SimpleRole ownerRole = new SimpleRole("owner");
contract.registerRole(ownerRole);

Main roles

The contract should define the basic roles: issuer, owner and creator. If at least one of these roles is missing, the contract will not pass the check.

The role of the issuer is serialized in the definition section and can not be changed in contract revisions. The roles of the owner and the creator is serialized in the state section and can change with new contract revisions. To change the owner role, you must have permission ChangeOwnerPermission.

Methods set keys of issuer role:

Role setIssuerKeys(Collection<?> keys)

Role setIssuerKeys(Object... keys)

  • keys to set "issuer" role to;
  • returns issuer role.

Use the code:

contract.setIssuerKeys(publicKeys);

Method get issuer role:

Role getIssuer()

  • returns issuer role.

Use the code:

Role issuerRole = contract.getIssuer();

Methods set keys of owner role:

Role setOwnerKey(Object keyOrRecord)

Role setOwnerKeys(Collection<?> keys)

Role setOwnerKeys(Object... keys)

  • keyOrRecord is key or key record to set "owner" role to;
  • keys to set "owner" role to;
  • returns owner role.

Use the code:

contract.setOwnerKeys(publicKeys);

Method get owner role:

Role getOwner()

  • returns owner role.

Use the code:

Role ownerRole = contract.getOwner();

Methods set keys of creator role:

Role setCreatorKeys(Collection<?> keys)

Role setCreatorKeys(Object... keys)

  • keys to set "creator" role to;
  • returns creator role.

Use the code:

contract.setCreatorKeys(publicKeys);

Methods set creator role:

Role setCreator(Collection records)

Role setCreator(Role role)

  • records is key records to set "creator" role to;
  • role is creator role;
  • returns creator role.

Use the code:

Set<KeyRecord> keyRecords = new HashSet<>();
keyRecords.add(new KeyRecord(publicKey));
contract.setCreator(keyRecords);

Method get creator role:

Role getCreator()

  • returns creator role.

Use the code:

Role creatorRole = contract.getCreator();

Also, the main roles in contract can be established using the methods createRole and registerRole.

Use the code:

Role issuerRole = new SimpleRole("issuer");
Role ownerRole = new SimpleRole("owner");
contract.createRole("issuer", issuerRole);
contract.registerRole(ownerRole);

Permissions

Permissions provide roles for the ability to perform certain actions with a contract, such as: change numbers, change owner, modify data, revoke, split-join and change of fields. When you attempt to register a new contract revision, all changes are checked for compliance with their contractual permissions. If all changes made correspond to the permissions of the contract and there are signatures corresponding to the role permissions used, then the contract is authorized for registration.

Permission to change numbers

Permission allows to change some numeric (as for now, integer) field, controlling it's range and delta. This permission could be used more than once allowing for different roles to change in different range and directions.

Create new permission for change some numeric field:

ChangeNumberPermission(Role role, Binder params)

  • role allows to permission;
  • params is parameters of permission: field_name, range (min_value, max_value) and delta (min_step, max_step).

Use the code:

SimpleRole ownerRole = new SimpleRole("owner", privateKeys);
Binder params = new Binder();
params.set("min_value", 1);
params.set("max_step", -1);
params.set("field_name", "units");
ChangeNumberPermission ChangeNumberPerm = new ChangeNumberPermission(ownerRole, params);
contract.addPermission(ChangeNumberPerm);

Permission to change owner

Permission allows to change and remove owner role of contract.

Create new permission for change owner role:

ChangeOwnerPermission(Role role)

  • role allows to permission.

Use the code:

SimpleRole ownerRole = new SimpleRole("owner", privateKeys);
ChangeOwnerPermission changeOwnerPerm = new ChangeOwnerPermission(ownerRole);
contract.addPermission(changeOwnerPerm);

Permission to modify data

Permission allows to change some set of fields. Field values can be limited to a list of values.

Create new permission for change some set of fields:

ModifyDataPermission(Role role, Binder params)

  • role allows to permission;
  • params is parameters of permission: fields is map of field names and lists of allowed values.

Use the code:

SimpleRole issuerRole = new SimpleRole("issuer", privateKeys);
HashMap<String, Object> fieldsMap = new HashMap<>();
fieldsMap.put("units", null);
Binder modifyDataParams = Binder.of("fields", fieldsMap);
ModifyDataPermission modifyDataPerm = new ModifyDataPermission(issuerRole, modifyDataParams);
contract.addPermission(modifyDataPerm);

Method adds field to the allowed for change:

ModifyDataPermission addField(String fieldName, List values)

  • fieldName is name of field allowed for change;
  • values is list of allowed values for adding field.

Use the code:

SimpleRole issuerRole = new SimpleRole("issuer", privateKeys);
ModifyDataPermission modifyDataPerm = new ModifyDataPermission(issuerRole, modifyDataParams);
modifyDataPerm.addField("units", null);
contract.addPermission(modifyDataPerm);

Permission to revoke

Permission allows to revoke contract.

Create new permission for revoke contract:

RevokePermission(Role role)

  • role allows to permission.

Use the code:

SimpleRole ownerRole = new SimpleRole("owner", privateKeys);
RevokePermission revokePerm = new RevokePermission(ownerRole);
contract.addPermission(revokePerm);

Permission to split-join

Permission to split and join contracts with the split and join of the value of a certain numeric field of contracts. For contracts with a certain number field ("amount") the following is allowed:

  • several multiple revisions can be legitimately derived (“split”) from any revision only if their total "amount" is the same as the "amount" of the base revision;
  • several multiple revisions can be legitimately “joined” if the result "amount' is the same as the sum of "amounts" in the base revisions and the values of the revisions fields listed in join_match_fields parameter is equals.

Create new permission for change some numeric field:

SplitJoinPermission(Role role, Binder params)

  • role allows to permission;
  • params is parameters of permission: field_name, min_value (default 0), min_unit (default 0.000000001), join_match_fields (default "state.origin").

Use the code:

RoleLink ownerLink = new RoleLink("@owner_link","owner");
Binder params = new Binder();
params.set("min_value", 1);
params.set("min_unit", 1);
params.set("field_name", "amount");
List <String> listFields = new ArrayList<>();
listFields.add("state.origin");
params.set("join_match_fields", listFields);
SplitJoinPermission splitJoinPerm = new SplitJoinPermission(ownerLink, params);
contract.addPermission(splitJoinPerm)

References

The References feature allows, from some contract (A), to refer and require the presence of another contract (B), which can be identified in different ways (existing contracts for hashId, contracts from the transaction – by transactional_id). Additionally, it allows you to verify the compliance of the contract (B) with the various reference conditions specified in contract (A):

graph LR A["Contract A"]-->B["Contract B"]

The references can be defined in the definition, state or transactional contract sections.

You can add references to the contract in several ways. You can use the Contracts Service and also you can use the following methods.

Method add reference to the references list of the contract:

void addReference(Reference reference)

  • reference reference to add.

Method remove reference to the references list of the contract:

void removeReference(Reference reference)

  • reference reference to remove.

Create contract with reference from DSL

After you have dsl file with references added as desribed above you can create contract using code:

Contract contract = Contract.fromDslFile("DSLWithReference.yml");
contract.addSignerKey(somePrivateKey);
contract.seal();

This contract will have reference object with conditions and permission for roles reqired referenced contract. Then you need to add referenced contract certification_contract using

contract.findReferenceByName("certification_contract").addMatchingItem(certification_contract);

or directly add to transaction pack:

TransactionPack tp = contract.getTransactionPack();
tp.addReferencedItem(certification_contract);

Create contract with reference programmatically

Howhever, it is possible to create contracts with references without dsl files, directly from the code:

// create contract reference will be added to
Contract contract = new Contract(somePrivateKeys);
// create reference
Reference reference = new Reference(contract);
reference.name="certification_contract";
reference.type = Reference.TYPE_EXISTING_DEFINITION;
// create conditions for reference
List <String> listConditions = new ArrayList<>();
listConditions.add("ref.definition.issuer == \"26RzRJDLqze3P5Z1AzpnucF75RLi1oa6jqBaDh8MJ3XmTaUoF8R\"");
listConditions.add("ref.definition.data.issuer == \"ApricoT\"");
listConditions.add("ref.definition.data.type == \"chief accountant assignment\"");
Binder conditions = new Binder();
conditions.set("all_of", listConditions);
// add conditions to reference
reference.setConditions(conditions);
// add referenced contract to the reference
reference.addMatchingItem(certification_contract);
// add reference to the contract
contract.addReference(reference);
// create permission for role with required reference
SimpleRole ownerRole = new SimpleRole("owner", ownerPrivateKeys);
ownerRole.addRequiredReference("certification_contract", Role.RequiredMode.ALL_OF);
ChangeOwnerPermission changeOwnerPerm = new ChangeOwnerPermission(ownerRole);
contract.addPermission(changeOwnerPerm);
// sign and seal contract
contract.addSignerKey(somePrivateKey);
contract.seal();