Path constraints

You can use PathConstraints to restrict the Path that is allowed in a transaction. The smart contract defines a set of PathConstraints for each Primary state status, for example when in status X you can follow PathConstraint A or B, but when you are in state Y you can only follow PathConstraint C.

In order to pass verification, the Path in the transaction must comply with at least one of the allowed PathConstraints for the status of the Primary input state.

PathConstraints are implemented as follows:

ContractUtils.kt:


class PathConstraint<T: StatusState>(val command: CommandData,
                                     val outputStatus: Status?,
                                     val inputMultiplicityConstraint: MultiplicityConstraint = MultiplicityConstraint(),
                                     val outputMultiplicityConstraint: MultiplicityConstraint = MultiplicityConstraint(),
                                     val additionalStatesConstraints: Set<AdditionalStatesConstraint> =  setOf()){

    infix fun allows(p: Path<T>): Boolean { ... }

    infix fun doesNotAllow(p: Path<T>): Boolean = !this.allows(p)

    private fun additionalStatesCheck(constraints: Set<AdditionalStatesConstraint>, additionalStates: Set<AdditionalStates>) :Boolean{ ... }
}

Where:

  • command is the class of the command required.
  • outputStatus is the outputStatus of the Primary state that is required.
  • inputMultiplicityConstraint defines the range of number of inputs of Primary type that is required.
  • outputMultiplicityConstraint defines the range of number of outputs of Primary type that is required.
  • additionalStatesConstraint defines which additional states must be present in the transaction.

A Path in a transaction will only be allowed by the PathConstraint if it passes all these requirements.

additionalStatesConstraint are implemented as follows:

ContractUtils.kt:

class AdditionalStatesConstraint(val type: AdditionalStatesType ,
                                 val statesClass: Class<out ContractState>,
                                 val requiredNumberOfStates: MultiplicityConstraint = MultiplicityConstraint()) {

    infix fun isSatisfiedBy(additionalStates: AdditionalStates ):Boolean {...}

    infix fun isNotSatisfiedBy (additionalStates: AdditionalStates): Boolean = !isSatisfiedBy(additionalStates)
}

Where:

  • type is INPUT, OUTPUT or REFERENCE.
  • statesClass is the required type of the additional states.
  • requiredNumberOfStates defines how many AdditionalStates of this type are allowed using a MultiplicityConstraint.

MultiplicityConstraint are defined as follows:

ContractUtils.kt:

class MultiplicityConstraint(val from: Int = 1,
                             val bounded: Boolean = true,
                             val upperBound: Int = from){

    infix fun allows(numberOfStates: Int): Boolean { ... }

    infix fun doesNotAllow(numberOfStates: Int): Boolean = !this.allows(numberOfStates)
}

Where:

  • from is the minimum number of states.
  • bounded specifies if there is an upper limit.
  • upperbound specifies the upper bound, which is only applied if bounded is true.

Note, the structure above allows for quite complex definition of what is allowed, in most cases these won’t be needed. To simplify the use of PathConstraints most properties are defaulted. So for example you can specify a Path constraint simply as:

PathConstraint(Commands.Reject(), REJECTED)

Which would default to:

  • 1 Input of Primary State type.
  • 1 output Primary State type.
  • No additional states required.

Or they could get much more complicated as in this example from the ContractUtils test scripts:

PathConstraint(Commands.Command2(), TestState2A.TestStatus.STATUSA2, additionalStatesConstraints = setOf(
        AdditionalStatesConstraint(AdditionalStatesType.INPUT, TestState2B::class.java, MultiplicityConstraint(2, false)),
        AdditionalStatesConstraint(AdditionalStatesType.REFERENCE, TestState2C::class.java),
        AdditionalStatesConstraint(AdditionalStatesType.OUTPUT, TestState2D::class.java)
))

Was this page helpful?

Thanks for your feedback!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.

We're sorry this page wasn't helpful. Let us know how we can make it better!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Create an issue

Create a new GitHub issue in this repository - submit technical feedback, draw attention to a potential documentation bug, or share ideas for improvement and general feedback.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.