Project

General

Profile

Using references

First, you define references in contract definition, like this:

 # reference contains the conditions which must be specified for the other contracts to link
 reference:
  name: ref_name
  where:
    - condition

condition is (EBNF3):

condition = (all_of | any_of | simple condition)

all_of = "all_of", {condition}

any_of = "any_of", {condition} 

simple condition = (operation_expression | operator_expression)

operation_expression = field_selector, right_operation

operator_expression = field_selector, operator, (constant | field_selector)

constant = ("null" | number | string | true | false)

field_selector = ("this" | "ref" | some_ref_name), ("definition", ".",  field_name) | ("state", ".", field_name)

field_name = (special_name | "data.", user_field_name)

user_field_name = any_string_id

special_name = "created_at", "origin", "creator", "issuer", "owner", etc.....

operation = "defined" | "undefined" 

operator = "<" | ">" | "<=" | ">=" | "!=" | "==", "matches"

The reference exists only if some contract in the pack matches all conditions. The matches operator is used when left and right operands are roles, and means the left operand role could be performed by the signing keys and matches the right role, typically, the role defined in the referenced contract.

Field selector meanings:

  • this - the contract where the reference is defined
  • ref - the reference being defined (one that is defined by this section)
  • some_ref_name - any other reference defined in this contract. The circular references must be detected early, e.g. after 16 hops the critical error should be raised and contract checking should be aborted.

1. Usage in permissions:

The references could extend permission by adding tall_of and any_of additional requires section with the same all_of or any_of list of references that must be resolved for the permission to be effective, for example:

reference:
  name: certification_contract
  where:
     all_of:
        - ref.defnition.issuer == address
        - ref.definition.data.issuer == "Roga & Kopita"
        - ref.definition.data.type == "chief accountant assignment"
        - this.owner matches ref.state.data.accountant 

and then in the permissions:

    split_join:
      - role: 
           type: ALL_OF
              roles:
                 - owner
           requires:
               all_of:
                 - ceritfication_contract

        # Mimimum splittable value. It means that we can split 10 coints to 9.99 and 0.01. Note that the system now
        # only allow values not lessser tha. 0.000`000`001, e.g. 1E-9, 1 nano-unit of something.
        min_value: 0.01 # one cent is a minimum
        # all split values must not differ to a value less than this unit. Actually, it should always
        # be a power of 10, like 0.0001 The system checks that any split value's ULP is no less than that
        # (ULP is Unit in Last Position, common term. For example ULP(117.223) is 0.001
        # is greated tham this. In our case, it allows splitting 10 to 5.001 and 4.999, but not to 5.1234 and 4.9866
        # as the ulp will be 0.0001 for 4.8966. In most cases it is practical to have same min_unit and to min_value
        # as the 10th power
        min_unit: 0.001

        # The name of the field to change
        field_name: amount

        # this way we forbid any later emissions: only parts from this contract could be joined.
        # adding other field to this allow additional emission:
        join_match_fields:
          - state.origin

This requires a reference, the accountant assignment contract issued by some key with a given type, where the accountant is set to some role that could be played by the current owner, e.g. if the owner of this contract matches the accountant in the referenced contract, if could use this split_join.

Note that the same could be achieved ny simply specifying the origin of the accountant assignment contract:

reference:
  name: certification_contract
  where:
     all_of:
        - ref.origin == some_hashid
        - this.owner matches ref.state.data.accountant 

This is simpler, but restricts all future assignment contracts to be of the specified chain, while the longer first version allows to issue several such contracts and therefore have more than one accountant license at a time.

Create contract with reference from dsl

After you have dsl file with references added as desribed above you can create contract usinf 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 programmly

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 Contarct(somePrivateKeys);

// create reference
Reference reference = new Reference(contract);
reference.name="certification_contract";
reference.type = Reference.TYPE_EXISTING;

// create conditions for reference
List <String> listConditions = new ArrayList<>();
listConditions.add("ref.definition.issuer == \"26RzRJDLqze3P5Z1AzpnucF75RLi1oa6jqBaDh8MJ3XmTaUoF8R\"");
listConditions.add("ref.definition.data.issuer == \"Roga & Kopita\"");
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 contarct
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 contarct
contract.addSignerKey(somePrivateKey);
contract.seal();