Contracts Service


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.4 dependency from Universa public Maven repository. See more details at Maven repository page.

This feature is available in package com.icodici.universa.contract, as the ContractsService class; see it on Github.

Overview

The Contracts Service is a service that helps to create various types of contracts, implement and execute various common operations with them.

The Contracts Service allows you to:

  • create fixed supply token-type contract;
  • create mintable token-type contract;
  • splitting and joining token-type contracts;
  • create shares-type contract;
  • create notary-type contract;
  • create contract with two signatures;
  • create Slot contract;
  • create UNS1 contract;
  • create Follower contract;
  • revocation of contracts;
  • exchange some contracts;
  • add references;
  • create parcel and paying parcel;
  • create rate limit disabling contract;
  • and possibly other features.

Token-type contracts

The Contracts Service makes it possible to create a simple (non-mintable, fixed supply) template token contract as easy as a mintable (dynamic-supply) template token contract. And also allows to split and join token amounts, for example, to make or receive payments.

Fixed-supply token contract

Creates a simple (fixed supply) template token contract for given keys:

Contract createTokenContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, String amount, Double minValue, String currency, String name, String description)
Contract createTokenContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, String amount, Double minValue)
Contract createTokenContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, String amount)
  • issuerKeys is issuer private keys;
  • ownerKeys is owner public keys;
  • amount is issued tokens amount;
  • minValue is minimum token value;
  • currency is token currency code;
  • name is token currency name;
  • description is token currency description;
  • returns signed and sealed contract, ready for register.

The method creates a fixed-supply templated token contract, signed and sealed and ready for register, containing:

  • issuer, creator and owner roles properly defined;
  • change_owner permission for owner, revoke permissions for owner and issuer and split_join permission for owner.

The Split_join permission has the following parameters:

params.set("min_value", minValue);
params.set("min_unit", minValue);
params.set("field_name", "amount");
listFields.add("state.origin");
params.set("join_match_fields", listFields);

By default expires_at time is set to 60 months from now.

The owner can split and join tokens, for example, to make payment or receive payments. To do this, you must use the split and join methods.

Use the code:

Contract tokenContract = ContractsService.createTokenContract(issuerPrivateKeys,ownerPublicKeys, "1000000");
ItemResult itemResult = client.register(tokenContract.getPackedTransaction(), 5000);

Mintable token contract

The “mintable token”-type contract lets you to issue multiple tokens contracts unlike the “fixed supply token“ that is only issued once. To create a templated mintable token-type contract, you must use the following methods:

Contract createMintableTokenContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, String amount, Double minValue, String currency, String name, String description)
Contract createMintableTokenContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, String amount, Double minValue)
Contract createMintableTokenContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, String amount)
  • issuerKeys contains the issuer private keys. Keep it the same for all issued contracts of the same currency!;
  • ownerKeys contains the owner public keys;
  • amount contains the initial token amoun;
  • minValue contains the minimum token value;
  • currency is token currency code. Keep it the same for all issued contracts of the same currency!;
  • name is token currency name;
  • description is token currency description;
  • returns signed and sealed contract, ready for register.

The method creates a templated mintable token-type contract, signed and sealed and ready for register, containing:

  • issuer, creator and owner roles properly defined;
  • change_owner permission for owner, revoke permissions for owner and issuer and split_join permission for owner.

Whenever you need to execute an additional emission of this token (“mint” more of this token), you should use the same create… methods, providing them with the same issuerKeys and currency. This will cause the newly-created tokens to be compatible (for split/join operations) with the previous emission(s).

Note: the previous methods to create the mintable tokens and to create the new emissions, createTokenContractWithEmission and createTokenEmission, are now deprecated.

Split_join permission has following params:

params.set("min_value", minValue);
params.set("min_unit", minValue);
params.set("field_name", "amount");
listFields.add("definition.issuer");
listFields.add("definition.data.currency");
params.set("join_match_fields", listFields);

Modify_data permission has following params fields: "amount".

By default expires_at time is set to 60 months from now.

The owner can split and join mintable token, for example, to make payment or receive payments. To do this, you must use the split and join methods.

Use the code:

Contract mintableTokenContract1 = ContractsService.createMintableTokenContract(issuerPrivateKeys, ownerPublicKeys, "300000000000");
Contract mintableTokenContract2 = ContractsService.createMintableTokenContract(issuerPrivateKeys, ownerPublicKeys, "100000000000");
ItemResult itemResult = client.register(mintableTokenContract1.getPackedTransaction(), 5000);
ItemResult itemResult = client.register(mintableTokenContract2.getPackedTransaction(), 5000);
Contract joinedContract = ContractsService.createJoin(mintableTokenContract1,mintableTokenContract2,"amount",ownerPrivateKeys)
itemResult = client.register(joinedContract.getPackedTransaction(), 5000);

Split method

This method creates a new revision of given contract, and splits it to the pair of contracts with split amount:

Contract createSplit(Contract c, String amount, String fieldName, Set<PrivateKey> keys)
Contract createSplit(Contract c, String amount, String fieldName, Set<PrivateKey> keys, boolean andSetCreator)
  • c is a contract that must be split. Note that this contract must have a split_join permission for the specified keys;
  • amount is value that should be split from given contract;
  • fieldName is name of field that should be split;
  • keys is one or more keys that together can play the role assigned to the split_join permission;
  • andSetCreator if true set owners as creator in both contracts;
  • returns a ready sealed contract, that must be registered with Universa to complete the split contract procedure.
graph TB C["Contract C

100"]-->A["Contract A

70"] C["Contract C

100"]-->B["Contract B

30"]

Upon the successful registration the source contract will be revoked.

Use the code:

Contract contractC = ContractsService.createTokenContract(issuerPrivateKeys,ownerPublicKeys, "100");
ItemResult itemResult = client.register(contractC.getPackedTransaction(), 5000);
Contract contractA = ContractsService.createSplit(contractC, "30", "amount", issuerPrivateKeys2, true);
Contract contractB = contractA.getNew().get(0);
itemResult = client.register(contractA.getPackedTransaction(), 5000);

Join method

This method creates a new revision of the first contract, updates the amount field with the sum of amount fields in the both contracts, and puts the second contract into the revoking items of the just-created new revision.

Contract createJoin(Contract contract1, Contract contract2, String fieldName, Set<PrivateKey> keys)
  • contract1 is a contract that must be joined to;
  • contract2 is a contract that is being joined. Note that this contracts must have a compatible split_join permission for the specified keys;
  • fieldName is name of field that should be joined;
  • keys is one or more keys that together can play the role assigned to the split_join permission;
  • returns a ready sealed contract, that must be registered with Universa to complete the split contract procedure.
graph TB A["Contract A

70"]-->C["Contract C

100"] B["Contract B

30"]-->C["Contract C

100"]

Upon the successful registration the contract A and contract B will be revoked.

Use the code:

Contract contractC = ContractsService.createJoin(contractA, contractB, "amount", issuerPrivateKeys2);
ItemResult itemResult = client.register(contractC.getPackedTransaction(), 5000);

Shares-type contract

You can very easily create a templated shares contract for the given keys. Use the method:

Contract createShareContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, String amount)
  • issuerKeys is issuer private keys;
  • ownerKeys is owner public keys;
  • amount is maximum shares number;
  • returns signed and sealed contract, ready for register.

The method creates a simple template share contract, signed and sealed and ready for register, containing:

  • issuer, creator and owner roles properly defined;
  • change_owner permission for owner, revoke permissions for owner and issuer and split_join permission for owner.

Split_join permission has the following parameters:

params.set("min_value", 1);
params.set("min_unit", 1);
params.set("field_name", "amount");
listFields.add("state.origin");
params.set("join_match_fields", listFields);

By default expires_at time is set to 60 months from now.

Use the code:

Contract shareContract = ContractsService.createShareContract(issuerPrivateKeys,ownerPublicKeys,"100");
ItemResult itemResult = client.register(shareContract.getPackedTransaction(), 5000);

Notary-type contract

Methods creates a simple notary contract for given keys, attach the data file to notary contract and attach the data file descriptions:

Contract createNotaryContract(Set issuerKeys, Set ownerKeys)

Contract createNotaryContract(Set issuerKeys, Set ownerKeys, List filePaths)

Contract createNotaryContract(Set issuerKeys, Set ownerKeys, List filePaths, List fileDescriptions)

  • issuerKeys is issuer private keys;
  • ownerKeys is owner public keys;
  • filePaths is path to data file;
  • fileDescriptions is data file descriptions.

Returns signed and sealed contract, ready for register.

The method creates a simple templated notary contract, signed and sealed and ready for register, containing:

  • issuer, creator and owner roles properly defined;
  • change_owner permission for owner and revoke permissions for owner and issuer.

By default expires_at time is set to 60 months from now.

Use the code:

Contract notaryContract = ContractsService.createNotaryContract(issuerPrivateKeys, ownerPublicKeys, fileName, fileDesc);
ItemResult itemResult = client.register(notaryContract.getPackedTransaction(), 5000);

Contract with two signatures

This method creates a contract which requires two signatures to present, to become valid (often called “multisig” in other architectures). It can not be registered until both parts of the deal have signed it. This feature allows you to make sure that both parties agree with the contract:

Contract createTwoSignedContract(Contract baseContract, Set<PrivateKey> fromKeys, Set<PublicKey> toKeys, boolean createNewRevision)
  • baseContract is base contract;
  • fromKeys is own private keys;
  • toKeys is foreign public keys;
  • createNewRevision create new revision from base contract if true;
  • returns contract with two signatures that should be send from first part to partner.

In order to understand how this looks in practice, consider in more detail: we have the opportunity to create a simple exchange contract aimed at sending something with the approval of the recipient before the transfer.

Participant A sends something (for example, money) to participant B and sends him a contract for two signatures. Participant B signs the contract, but he can not register it. Then participant B returns the contract to participant A.

Participant A signs the contract. Now a contract for two signatures can be registered with Universa.

sequenceDiagram Participant_A-->>Participant_B: Hello,confirm receipt Participant_B->>Participant_A: I agree, thanks

Participant A is confident and can prove that B has a copy of the receiving contract.

Use the code:

// participant A
Contract twoSignContract = ContractsService.createTwoSignedContract(baseContract, partAPrivateKeys, partBPublicKeys, false); 

// participant B
twoSignContract.addSignatureToSeal(partBPrivateKeys);

// participant A
twoSignContract.addSignatureToSeal(partAPrivateKeys);

ItemResult itemResult = client.register(twoSignContract.getPackedTransaction(), 5000);

Escrow contract

An Escrow contract involves a contract for the blocking of payment received from the depositor(customer) with a view to transferring it to another person the beneficiary(executor) in the event that the grounds provided for by the contract arise and with the participation of the third line escrow agent(arbitrator).

graph LR a1(Depositor) a2(Escrow agent) a3(Beneficiary) a1-.-a2 a1-.-a3 a2-.-a3

The fact of occurrence of the terms provided by the agreement is accepted by the joint decision of the depositor (customer) and the beneficiary(executor),for example:

graph LR a1(Depositor) a2(Escrow agent) a3(Beneficiary) a1-.-a2 a1===a3 a2-.-a3 style a1 fill:#98ff8f,stroke:#333,stroke-width:2px; style a2 fill:#ff7373,stroke:#333,stroke-width:2px; style a3 fill:#98ff8f,stroke:#333,stroke-width:2px;

or by the decision of the escrow agent(arbitrator) and any of the participants, as a result of which the payment becomes available to the beneficiary(executor):

graph LR a1(Depositor) a2(Escrow agent) a3(Beneficiary) a1-.-a2 a1-.-a3 a2===a3 style a1 fill:#ff7373,stroke:#333,stroke-width:2px; style a2 fill:#98ff8f,stroke:#333,stroke-width:2px; style a3 fill:#98ff8f,stroke:#333,stroke-width:2px;

Similarly, a decision is made to cancel the escrow contract, as a result of which the payment is returned to the depositor (customer):

graph LR a1(Depositor) a2(Escrow agent) a3(Beneficiary) a1===a2 a1-.-a3 a2-.-a3 style a1 fill:#98ff8f,stroke:#333,stroke-width:2px; style a2 fill:#98ff8f,stroke:#333,stroke-width:2px; style a3 fill:#ff7373,stroke:#333,stroke-width:2px;

In other words, the escrow contract allows you to enter into an agreement in which the depositor undertakes to transfer the payment to the escrow agent in order to fulfill the obligation of the depositor to transfer it to another person in favor of which the deposit is made (the beneficiary). And the escrow agent is obliged to transfer it to thebeneficiary if the grounds indicated in the contract arise, otherwise return the payment to the depositor.

Create Escrow contract

The Escrow contract is an External Escrow contract (container) that containing an Internal Escrow contract, as well as a payment contract or several payment contracts:

graph TD subgraph External Escrow contract Internal_Escrow_contract(Internal Escrow contract) Payment1(Payment contract 1) Payment2(Payment contract N) end

Method for create Escrow contract

Method createEscrowContract is basic, in most cases sufficient to create a escrow contract.

Method creates an external escrow contract containing an internal escrow contract. Contracts are linked by internal escrow contract origin.

To internal escrow contract establishes the owner role, on the basis of the quorum of 2 of 3 roles: customer, executor and arbitrator. This role is granted exclusive permission to change the value of the status field of internal escrow contract (state.data.status). Possible values for the internal escrow contract status field are: opened, completed and canceled. External and internal escrow contracts create for a expiration period of 5 years.

Contract createEscrowContract(Collection issuerKeys, Collection customerKeys, Collection executorKeys, Collection arbitratorKeys)

  • issuerKeys are issuer escrow contract private keys;
  • customerKeys are customer public keys;
  • executorKeys are executor public keys;
  • arbitratorKeys are arbitrator public key.

Returns external escrow contract.

If necessary, the contents and parameters (expiration period, for example) of escrow contracts can be changed before sealing and registration.

If internal escrow contract origin has changed, need re-create external escrow contract by Contracts service method createExternalEscrowContract.

Use the code:

Contract escrow = ContractsService.createEscrowContract(issuerPrivateKeys, customerPublicKeys, executorPublicKeys, arbitratorPublicKeys);

Method for create Internal Escrow contract

Method createInternalEscrowContract is used to create a escrow contract without creating a container (external escrow contract).

Method сreates Internal escrow contract for a expiration period of 5 years. To internal escrow contract establishes the owner role, ListRole on the basis of the quorum of 2 of 3 roles: customer, executor and arbitrator. This role is granted exclusive permission to change the value of the status field of internal escrow contract (state.data.status). Possible values for the internal escrow contract status field are: opened, completed and canceled.

Contract createInternalEscrowContract( Collection issuerKeys, Collection customerKeys, Collection executorKeys, Collection arbitratorKeys)

  • issuerKeys are issuer escrow contract private keys;
  • customerKeys are customer public keys;
  • executorKeys are executor public keys;
  • arbitratorKeys are arbitrator public keys.

Returns internal escrow contract.

If necessary, the contents and parameters (expiration period, for example) of escrow contract can be changed before sealing and registration.

If internal escrow contract origin has changed, need re-create external escrow contract (if used) by createExternalEscrowContract.

Use the code:

Contract escrow = ContractsService.createInternalEscrowContract(issuerPrivateKeys, customerPublicKeys, executorPublicKeys, arbitratorPublicKeys);

Modification Escrow contract

If necessary, fields containing additional data can be added to the escrow contract (external and internal). Also, the contents and parameters of escrow contract can be changed.

Example adding field:

internalEscrow.getStateData().set("description", "internal escrow contract");
internalEscrow.seal();

Changes in the external contract do not impose any special restrictions. If you change the internal contract (if it is root and the origin changes), you need re-create external escrow contract (if used).

Method createExternalEscrowContract сreates external escrow contract for a expiration period of 5 years. External escrow contract includes internal escrow contract. Contracts are linked by internal escrow contract origin. Method should be used to re-create external escrow contract, if internal escrow contract origin has changed.

Contract createExternalEscrowContract(Contract internalEscrow, Collection issuerKeys)

  • internalEscrow is internal escrow contract;
  • issuerKeys are issuer escrow contract private keys.

Returns external escrow contract.

Use the code:

escrow = ContractsService.createExternalEscrowContract(internalEscrow, issuerPrivateKeys);

Payment contract for Escrow contract

The Escrow contract may include (or be associated with) one or more payment contracts.

Add payment contract

Method addPaymentToEscrowContract check external escrow contract and add payment contract to it.

To payment contract is added Transactional section with 2 references: send_payment_to_executor, return_payment_to_customer. The owner of payment contract is set to ListRole contains customer role with return_payment_to_customer reference and executor role with send_payment_to_executor reference. Any of these roles is sufficient to own a payment contract.

boolean addPaymentToEscrowContract( Contract escrow, Contract payment, Collection paymentOwnerKeys, Collection customerKeys, Collection executorKeys)

  • escrow contract (external) to use with payment. Must be returned from createEscrowContract or createExternalEscrowContract;
  • payment contract to update. Must not be registered (new root or new revision);
  • paymentOwnerKeys are keys required for use payment contract (usually, owner private keys). May be null, if payment will be signed later;
  • customerKeys are customer public keys of escrow contract;
  • executorKeys are executor public keys of escrow contract.

Returns result of checking external escrow contract and adding payment to it.

Use the code:

Contract escrow = ContractsService.createEscrowContract(issuerPrivateKeys, customerPublicKeys, executorPublicKeys, arbitratorPublicKeys);

boolean result = ContractsService.addPaymentToEscrowContract(escrow, payment, customerPrivateKeys, customerPublicKeys, executorPublicKeys);

Modify payment contract

Method modifyPaymentForEscrowContract modifies payment contract by making ready for escrow. Method is used for payments of internal escrow contract.

To payment contract is added Transactional section with two references: send_payment_to_executor, return_payment_to_customer. The owner of payment contract is set to ListRole contains customer role with return_payment_to_customer reference and executor role with send_payment_to_executor reference. Any of these roles is sufficient to own a payment contract.

Contract modifyPaymentForEscrowContract( Contract escrow, Contract payment, Collection paymentOwnerKeys, Collection customerKeys, Collection executorKeys)

Contract modifyPaymentForEscrowContract( String escrowOrigin, Contract payment, Collection paymentOwnerKeys, Collection customerKeys, Collection executorKeys)

  • escrow is internal escrow contract to use with payment. Must be returned from ContractsService.createInternalEscrowContract;
  • escrowOrigin is origin (base64 string) of internal escrow contract to use with payment;
  • payment contract to update. Must not be registered (new root or new revision);
  • paymentOwnerKeys are keys required for use payment contract (usually, owner private keys). May be null, if payment will be signed later;
  • customerKeys are customer public keys of escrow contract;
  • executorKeys are executor public keys of escrow contract.

Returns payment contract ready for escrow.

Use the code:

Contract escrow = ContractsService.createInternalEscrowContract(issuerPrivateKeys, customerPublicKeys, executorPublicKeys, arbitratorPublicKeys);

payment = ContractsService.modifyPaymentForEscrowContract(escrow, payment, customerPrivateKeys, customerPublicKeys, executorPublicKeys);

Complete escrow contract

Method completeEscrowContract completes escrow contract. All linked payments are made available to the executor. For registration completed escrow contract require quorum of 2 of 3 roles: customer, executor and arbitrator. After the completion of the escrow contract, it can not be canceled or reopened.

public static Contract completeEscrowContract(Contract escrow)

  • escrow contract (external with internal or only internal) to complete. Must be registered for creation new revision.

Returns completed internal escrow contract or null if error occurred.

Use the code:

Contract completedEscrow = ContractsService.completeEscrowContract(escrow);

Cancel escrow contract

Method cancelEscrowContract сancels escrow contract. All linked payments are made available to the customer. For registration canceled escrow contract require quorum of 2 of 3 roles: customer, executor and arbitrator. After the cancellation of the escrow contract, it can not be completed or reopened.

public static Contract cancelEscrowContract(Contract escrow)

  • escrow contract (external with internal or only internal) to cancel. Must be registered for creation new revision.

Returns canceled internal escrow contract or null if error occurred.

Use the code:

Contract canceledEscrow = ContractsService.cancelEscrowContract(escrow);

Transfer payment contract to new owner

Method takeEscrowPayment transfers payment contract to new owner on the result of escrow. Use payment contract that was added to external escrow contract by addPaymentToEscrowContract or linked with internal escrow contract by modifyPaymentForEscrowContract.

Executor can take the payment contract, if escrow contract are completed. Customer can take the payment contract, if escrow contract are canceled. For registration payment contract (returned by this method) add result internal escrow contract by TransactionPack.addReferencedItem.

Contract takeEscrowPayment(Collection newOwnerKeys, Contract payment)

  • newOwnerKeys are private keys of new owner of payment;
  • payment contract to take by new owner. Must be registered for creation new revision.

Returns new revision of payment contract with new owner.

Use the code:

Contract takedPayment = ContractsService.takeEscrowPayment(ownerKeys, payment);

Examples

The following are examples of two standard methods of using the escrow contract.

The first way involves creating an external escrow contract and adding payment contracts directly to it. This allows you to register escrow in the one parcel. The example also shows the completion of escrow contract and the transfer of payment contract to the executor.

// create external escrow contract
Contract escrow = ContractsService.createEscrowContract(issuerPrivateKeys, customerPublicKeys, executorPublicKeys, arbitratorPublicKeys);

// add payment to escrow contract
boolean result = ContractsService.addPaymentToEscrowContract(escrow, payment, customerPrivateKeys, customerPublicKeys, executorPublicKeys);
assertTrue(result);

node.registerItem(escrow);

// complete escrow contract(by external escrow contract)
Contract completedEscrow = ContractsService.completeEscrowContract(escrow);

// sign completed internal escrow contract by issuer
completedEscrow.addSignatureToSeal(issuerPrivateKeys);

// sign completed internal escrow contract by customer
completedEscrow.addSignatureToSeal(customerPrivateKeys);

// sign completed internal escrow contract by executor
completedEscrow.addSignatureToSeal(executorPrivateKeys);

// register signed escrow contract
node.registerItem(completedEscrow);

// transfer payment to executor
Contract newPayment = ContractsService.takeEscrowPayment(executorPrivateKeys, payment);

// internal escrow contract for checking references
newPayment.getTransactionPack().addReferencedItem(completedEscrow);

// register result executor payment
node.registerItem(newPayment); 

The second way is to create an internal escrow contract and link payment contracts with it. This allows you to abandon the use of an external escrow contract (container). Also in the example is the cancellation of escrow contract and the return of payment contract to the customer.

// create internal escrow contract
Contract escrow = ContractsService.createInternalEscrowContract(issuerPrivateKeys, customerPublicKeys, executorPublicKeys, arbitratorPublicKeys);

// modify payment for escrow contract
payment = ContractsService.modifyPaymentForEscrowContract(escrow, payment, customerPrivateKeys, customerPublicKeys, executorPublicKeys);

// register escrow contract and payment
node.registerItem(escrow);
node.registerItem(payment);

// cancel escrow contract(by external escrow contract)
Contract canceledEscrow = ContractsService.cancelEscrowContract(escrow);

// sign canceled internal escrow contract by issuer
canceledEscrow.addSignatureToSeal(issuerPrivateKeys);

// sign canceled internal escrow contract by customer
canceledEscrow.addSignatureToSeal(customerPrivateKeys);

// sign canceled internal escrow contract by arbitrator
canceledEscrow.addSignatureToSeal(arbitratorPrivateKeys);

// register signed escrow contract
node.registerItem(canceledEscrow);

// return payment to customer
Contract newPayment = ContractsService.takeEscrowPayment(customerPrivateKeys, payment);

// internal escrow contract for checking references
newPayment.getTransactionPack().addReferencedItem(canceledEscrow);

// register result customer payment
node.registerItem(newPayment);

Slot contract

The “Slot contract” is one of several types of smart contracts that can be run on the node. Slot contract provides paid storing of other contracts at the distributed store, control storing time and control storing revisions of tracking contract.

Method creates a ready-made Slot contract with specified permissions and values:

SlotContract createSlotContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, NSmartContract.NodeInfoProvider nodeInfoProvider)
  • issuerKeys is issuer private keys;
  • ownerKeys is owner public keys;
  • nodeInfoProvider is node provider info;
  • returns ready Slot contract.

Returns Slot contract has change_owner, revoke and modify_data with special slot fields permissions. Sets issuerKeys as issuer, ownerKeys as owner.

UNS1 contract

Name Service provides operation of decentralized storage for records of unique names that allow you to identify keys, address and origin of the contract. UNS1 contract is used to manage and for payment of registration names in this distributed store.

The method creates ready simple UNS1 contract with need permissions and values:

UnsContract createUnsContract(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, NSmartContract.NodeInfoProvider nodeInfoProvider)
  • issuerKeys is issuer private keys;
  • ownerKeys is owner public keys;
  • nodeInfoProvider is node provider info;
  • returns ready UNS1 contract.

Returns UNS1 contract has change_owner, revoke and modify_data with special fields permissions. Sets issuerKeys as issuer, ownerKeys as owner.

The method creates a UNS1 contract ready to register a unique contract name, with the necessary permissions and values:

UnsContract createUnsContractForRegisterContractName(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, NSmartContract.NodeInfoProvider nodeInfoProvider, String name, String description, String URL, Contract namedContract)
  • issuerKeys is issuer private keys.
  • ownerKeys is owner public keys.
  • nodeInfoProvider is node provider info;
  • name is name for registration.
  • description is description associated with name for registration.
  • URL is URL associated with name for registration.
  • namedContract is named contract.
  • returns ready UNS1 contract

Returns UNS1 contract has change_owner, revoke and modify_data with special fields permissions. Sets issuerKeys as issuer, ownerKeys as owner.

Also added UNS name for registration associated with contract (by origin).

For example:

graph LR A["unique name"]-->B["contract (by origin)"] subgraph UNS1 contract A["unique name"] end

The method creates a UNS1 contract ready to register the unique name of the key (address), with the necessary permissions and values:

UnsContract createUnsContractForRegisterKeyName(Set<PrivateKey> issuerKeys, Set<PublicKey> ownerKeys, NSmartContract.NodeInfoProvider nodeInfoProvider, String name, String description, String URL, AbstractKey namedKey)
  • issuerKeys is issuer private keys;
  • ownerKeys is owner public keys;
  • nodeInfoProvider is node provider info;
  • name is name for registration;
  • description is description associated with name for registration;
  • URL is URL associated with name for registration;
  • namedKey is is named key;
  • returns ready UNS1 contract.

Returns UNS1 contract has change_owner, revoke and modify_data with special fields permissions. Sets issuerKeys as issuer, ownerKeys as owner.

Also added UNS name for registration associated with key (by addresses).

For example:

graph LR A["unique name"]-->B["key (by addresses)"] subgraph UNS1 contract A["unique name"] end

Follower contract

The “Follower contract” is one of several types of smart contracts that can be run on the node. Follower contract is a contract that tracks contract registrations in any chain of contracts in the network for payment, when a new event occurs, the contract sends a request to the URL specified by the user, which contains the body of the new registered revision.

The cost of servicing the Follower contract

The Follower contract works on the pre-paid basis.

When Follower contract is first created, it must be accompanying with a initial payment in the paying parcel, which should cover at least 40 origin-days, or cost 4 callbacks. For example, in the simplest case, at the moment the minimum cost will be 200 U, which corresponds to the cost of 40 OD or 4 callbacks.

After the registration of the Follower contract, the payment is spent on storage and on calling the callback.

If the balance on the account is less than the cost of the callback, then the contract will exist for some time. At this time you can replenish the account of the Follower contract. However, in this state, the contract will not cause a callback when registering a new revision of the tracked сontract.

Create Follower contract

Method createFollowerContract create and return ready FollowerContract contract with need permissions and values. FollowerContract is used for control and for payment for follow new revisions from some contract chains by origin.

FollowerContract createFollowerContract(Set issuerKeys, Set ownerKeys, NSmartContract.NodeInfoProvider nodeInfoProvider)

  • issuerKeys is issuer private keys;
  • ownerKeys is owner public keys;
  • nodeInfoProvider is node provider info.

Returns FollowerContract with need permissions and values.

Put tracking origin to Follower contract

Method putTrackingOrigin put new tracking origin and his callback data (URL and callback public key) to the follower contract. If origin already contained in follower contract, old callback data is replaced. If callback URL already contained in follower contract, old callback key is replaced:

void putTrackingOrigin(HashId origin, String URL, PublicKey key)

  • origin for tracking;
  • URL for callback if registered new revision with tracking origin;
  • key for checking receipt from callback by network.

Use the code:

FollowerContract followerContract = ContractsService.createFollowerContract(followerIssuerPrivateKeys, followerIssuerPublicKeys, nodeInfoProvider);
FollowerContract.putTrackingOrigin(simpleContract.getOrigin(), "http://localhost:7777/follow.callback", callbackKey.getPublicKey());

Revocation of contracts

In order to revoke the contract (let's name it a “source contract”), it is necessary to create an revocation contract. To revoke the contract it is necessary that it has "revoke" permission, and the party invoking the private key's matching the role(s) for this permission. That revocation contract should be sent to Universa, causing the “source” contract will be revoked. Method for creating a revocation contract:

Contract createRevocation(Contract sourceContract, PrivateKey... keys)
  • sourceContract is a contract that must be revoked;
  • keys is one or more keys that together can play the role assigned to the revoke permission;
  • returns a ready sealed contract, that must be registered with Universa to complete the revocation contract procedure.

So, to revoke some contract:

  1. Call method createRevocation(Contract c, PrivateKey... keys) and get the revocation contract;
  2. Register the revocation contract in the Universa network.

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

Use the code:

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

ItemResult itemResult = client.register(revokeContract.getPackedTransaction(), 5000);

Exchanging the contracts

There is a thoroughly-defined and secure procedure when two parties want to exchange (swap) some contracts with each other; for example, Alice has 200 ABC tokens and want to receive 300 CDE tokens; and Bob has those 300 CDE tokens and want to receive 200 ABC tokens for them. This procedure makes it possible for them to execute such an exchange in an atomic way, so that neither of the two will may lose their tokens without receiving the new ones for them.

The procedure of such an exchange of contracts consists of three steps:

Step one

Methods prepares contracts with creating transactional section on the side of participant A, sign only one contract:

Contract startSwap(Contract contract1, Contract contract2, Set<PrivateKey> fromKeys, Set<PublicKey> toKeys)
Contract startSwap(List<Contract> contractsList1, List<Contract> contractsList2, Set<PrivateKey> fromKeys, Set<PublicKey> toKeys)
Contract startSwap(Contract contract1, Contract contract2, Set<PrivateKey> fromKeys, Set<PublicKey> toKeys, boolean createNewRevision)
Contract startSwap(List<Contract> contracts1, List<Contract> contracts2, Set<PrivateKey> fromKeys, Set<PublicKey> toKeys, boolean createNewRevision)
  • contract1 is own for calling part (participant A owned), existing or new revision of contract;
  • contract2 is foreign for calling part (participant B owned), existing or new revision contract;
  • contractsList1 is list of own for calling part (participant A owned), existing or new revision of contract;
  • contractsList2 is list of foreign for calling part (participant B owned), existing or new revision contract;
  • fromKeys is own for calling part (participant A keys) private keys;
  • toKeys is foreign for calling part (participant B keys) public keys;
  • createNewRevision if true - create new revision of given contracts. If false - use them as new revisions;
  • returns swap contract including new revisions of old contracts swapping between.

Should be send to participant B and he should go to step two of the exchange procedure.

Step two

Method sign contracts on the side of participant B:

Contract signPresentedSwap(Contract swapContract, Set<PrivateKey> keys)
  • swapContract is being processing swap contract, got from swapper1;
  • keys is own (belongs to participant B) private keys;
  • returns modified swapContract.

Participant B got swap contract from participant A and give it to service method.

Service signs the new contract where calling part was owner, store hashId of this contract.

Then added to reference of new contract, that will be own for calling part, contract_id and point it to contract that was own for calling part.

Then signs the second contract too.

Modified swapContract should be send back to participant A, and he should go to final step of the exchange procedure.

Final step

Method sign lost contracts on the side of participant A and finishing the exchange procedure:

Contract finishSwap(Contract swapContract, Set<PrivateKey> keys)
  • swapContract is being processing swap contract, now got back from participant B;
  • keys is own (belongs to participant A) private keys;
  • returns ready and sealed swapContract that should be register in the Universa to finish of the exchange procedure.

Participant A got swap contract from participant B. Sends it to service method and service method signs contract (that is inside swap contract) that will be own for calling part.

In order to understand how to do this in practice, imagine that participant A has a contract Contract1 and wants to exchange it for participant B contract, let's call him Contract2:

sequenceDiagram participant Participant_A participant Participant_B Participant_B-->>Participant_A: Contract2 Participant_A->>Participant_B: swapContract Participant_B->>Participant_A: modified swapContract

As a result, we have a deal with the following properties:

  1. The contract1 and the contract2 are approved at the same time or none of them is approved (atomic transaction);
  2. There is a guarantee that Participant A has a valid copy of the Contract2, and Participant B has a valid copy of the contract1, that is, they exchanged contracts.

Use the code:

swapContract = ContractsService.startSwap(contractA, contractB, privateKeysA, publicKeysB);

mSwapContract = ContractsService.signPresentedSwap(swapContract, privateKeysB);

swapContract = ContractsService.finishSwap(mSwapContract, privateKeysA);

References

This is the method to add a reference and referenced contract to the base contract. That is, this method creates a contract with a reference, and the reference specified in a reference contract that must satisfy the reference conditions.

Contract addReferenceToContract(Contract baseContract, Contract refContract, String refName, int refType, Binder conditions)
Contract addReferenceToContract(Contract baseContract, Contract refContract, String refName, int refType, List <String> listConditions, boolean isAllOfConditions)
  • baseContract is base contract for adding reference;
  • refContract is referenced contract (which must satisfy the conditions of the reference);
  • refName is name of reference;
  • refType is type of reference (section, may be TYPE_TRANSACTIONAL, TYPE_EXISTING_DEFINITION or TYPE_EXISTING_STATE;
  • conditions is conditions of the reference);
  • listConditions is list of strings with conditions of the reference;
  • isAllOfConditions is flag used if all conditions in list must be fulfilled (else - any of conditions);
  • returns ready and sealed contract.

When the returned contract is being unpacked, referenced contract verifies the compliance with the conditions of reference in the base contract.

For example:

graph LR A["baseContract

- this.owner == ref.owner
- this.state.data.int <= ref.state.data.int
- ref.definition.created_at > "2018-04-12 19:03:00""]-->B["refContract"]

Use the code:

List <String> listConditions = new ArrayList<>();

listConditions.add("ref.definition.data.issuer == \"ApricoT\"");
listConditions.add("ref.definition.data.type == \"chief accountant assignment\"");

ContractsService.addReferenceToContract(llcProperty, jobCertificate, "certification_contract", Reference.TYPE_EXISTING_DEFINITION, listConditions, true);

Parcel and paying parcel

Most of all operations in the Universa network, such as contracts registration, should be paid. Payment carried out by special contract. To successfully registration of your contract you need to created paid transaction named as Parcel and send Parcel to the Universa network.

And also you can create paying parcel is an extension to the Parcel structure allowing include additional payment field that will not be registered if the transaction will fail.

Parcel

Methods created paid transaction named as Parcel, which consist from contract you want to register and payment contract that will be spend to process transaction:

Parcel createParcel(Contract payload, Contract payment, int amount, Set<PrivateKey> keys)
Parcel createParcel(Contract payload, Contract payment, int amount, Set<PrivateKey> keys, boolean withTestPayment)
  • payload is prepared contract you want to register in the Universa;
  • payment is approved contract with U belongs to you;
  • amount is number of U you want to spend to register payload contract;
  • keys is own private keys, which are set as owner of payment contract;
  • param withTestPayment if true Parcel will be created with test payment;
  • returns parcel, it ready to send to the Universa.

Methods create paid transaction named as Parcel, which consist from prepared TransactionPack you want to register and payment contract that will be spend to process transaction:

Parcel createParcel(TransactionPack payload, Contract payment, int amount, Set<PrivateKey> keys)
Parcel createParcel(TransactionPack payload, Contract payment, int amount, Set<PrivateKey> keys, boolean withTestPayment)
  • payload is prepared TransactionPack you want to register in the Universa;
  • payment is approved contract withU belongs to you;
  • amount is number of U you want to spend to register payload contract;
  • keys is own private keys, which are set as owner of payment contract;
  • param withTestPayment if true Parcel will be created with test payment;
  • returns parcel, it ready to send to the Universa.

Registration of the returned parcel is described in Java API.

Parcel structure:

graph TD subgraph Parcel Payment Payload end

Use the code:

Parcel parcel = ContractsService.createParcel(contract.getTransactionPack(), paymentContract, 1, privateKeys, false);

Paying Parcel

Method create Paying parcel is an extension to the parcel structure allowing include additional payment field that will not be registered if the transaction will fail:

Parcel createPayingParcel(TransactionPack payload, Contract payment, int amount, int amountSecond, Set<PrivateKey> keys, boolean withTestPayment)
  • payload is prepared TransactionPack you want to register in the Universa;
  • payment is approved contract with U belongs to you;
  • amount is number of U you want to spend to register payload contract;
  • amountSecond is number of U you want to spend from second payment;
  • keys is own private keys, which are set as owner of payment contract;
  • withTestPayment if true {@link Parcel} will be created with test payment;
  • returns parcel, it ready to send to the Universa.

Registration of the returned paying parcel is described in Java API.

Paying Parcel structure:

graph TD subgraph Paying Parcel Payment subgraph Payload Second_payment end end

Use the code:

Parcel payingParcel = ContractsService.createPayingParcel(uns.getTransactionPack(), paymentContract, 1, nodeInfoProvider.getMinPayment("UNS1"), privateKeys, false);

Rate limit disabling contract

In the Universa network the limit of inquiries on a public key is established, you can make no more than 30 requests per minute, except for registration requests. However, if necessary, you can turn off the query limit for a time, for five minutes.The cost of five minute of unlimited use of the network is 5 U.

In order to disable the limit of queries in the Universa network, you need to create a rate limit disabling contract using the createRateLimitDisablingContract method and register it on the Universa network.

If you need to extend disable the limit of queries in the Universa network, you must create a new contract and register it.

Method createRateLimitDisablingContract creates Special contract to disable request limit in the Universa network:

Contract createRateLimitDisablingContract(PublicKey key, Contract payment, int amount, Set keys)

  • key is key for setting unlimited requests;
  • payment is approved contract with "U" belongs to you;
  • amount is number of "U" you want to spend to set unlimited requests for key;
  • keys is own private keys, which are set as owner of payment contract.

Returns a rate limit disabling contract for the key.

A rate limit disabling contract is registered without additional payment, because it is a specialized contract.

Method getRateLimitDisablingPayment returns the cost to disable the limit of queries in the Universa network:

int getRateLimitDisablingPayment()

Use the code:

Contract contractForUnlimitKey = ContractsService.createRateLimitDisablingContract( publicKey, payment, Config.getRateLimitDisablingPayment(), paymentKeys);

Release notes

3.4.8 (08.05.2018)

  • Added methods for payment parcel and slot contract creation

3.2.4b2 (27.03.2018)

  • Added possibility for creation simple contract's templates such as token, share and notary

3.0.1 (27.02.2018)

  • Collected and implemented methods for provide base contract's operations: revoke, split, join, swap, create parcel, create U