- General Java API
2018-10-28 16:10
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 checkreturn
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 tosignature
.
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 withdata
to be sign with keyreturns
prepared signature
In three steps:
- Create target signature using
ExtendedSignature.createTargetSignature
method. - Manually sign target signature with SHA512 and SHA3(384bit)
- 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 withdata
binary data signature is created forsavePublicKey
indicates if public key binary should be included into signed binaryreturns
target signature
static public byte[] of(byte[] targetSignature, byte[] sign, byte[] sign2)
targetSignature
is public key corresponding the key to sign withsign
target signature signed with SHA512sign2
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);
Role Link
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;roleName
is 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):
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();