The Scala core of the OpenLaw project contains two sub-projects,
openlawCoreJs subfolder). The
client library is well documented elsewhere in our docs.
shared sub-project contains four key components: (1) the oracles (in the
oracles folder), (2) a set of custom value types related to contracts and templates (in the
values folder), (3) the OpenLaw Markup Language parser and associated types and rules (in the
parser folder), and (4) the OpenLaw Virtual Machine (hereafter called the "VM") (in the
In order to understand these components, we need to understand some fundamental concepts within the OpenLaw protocol. The lifecycle of an agreement on the OpenLaw platform is as follows. It starts as a template: a legal agreement, marked up using the OpenLaw Markup Language, with empty fields corresponding to various provisions. When the user fills out some, but not all, of the fields, it becomes a draft. Once all fields have been filled out, the draft becomes a contract. A contract can then be sent to signatories and its signing will be registered and verified on the Ethereum blockchain. Signing by all parties will also cause any smart contracts embedded in the document to execute according to the provisions which the user has specified.
Under the hood, much of the workflow above is managed and moderated by the OpenLaw VM. The VM is a secure execution environment for contracts. It has several functions. First, it keeps track of the series of events over the contract's lifetime using the custom
OpenlawVmEvent type. The VM uses
Oracles to verify event requests. They will be discussed in greater detail below.
Second, the VM keeps track of the state of the contract itself at any given time using the
OpenlawVmState private variable. One important aspect of state involves the status of smart contracts embedded in the contract. The VM uses OpenLaw's Action API to query and store actions in the life of smart contracts. These actions may include starting, stopping, and resuming smart contract executions at time intervals which the user has specified.
In order for the legal agreement to be interpreted properly, the OpenLaw Markup Language, like many other computer languages, needs to know how to interpret and compile custom language types. The
VariableExecutionEngine and its sub-type the
OpenlawExecutionEngine help handle some of the logic for this. For example, the
processExecutedElement function in the
OpenlawExecutionEngine pattern matches on the type of the variable to provide specific execution instructions.
OpenlawVmProvider class contains just one method,
create, which returns an
OpenlawVm type. This is useful for integrating the
OpenlawVm into the server and other applications.
OpenLaw's oracles are designed in a modular way so that they can be integrated with mainnet, testnet, or other backends without much extra customization. In general, OpenLaw prefers to develop high-level components which are broadly functional, so that they can be plugged into a wide variety of environments and use cases, and the oracles are an example of that.
All oracles share a similar workflow:
Validate the event being passed to the VM. This is typically done using a cryptographic proof.
If the validation check passes, allow the event to occur.
Currently, the following oracles exist:
OpenlawOracle: This is a trait which
TemplateLoadOracle extend. It requires passing in the type of the event to be executed if the validation is successful. For example,
EthereumSmartContractOracle executes an
EthereumSmartContractCallEvent if successful.
EthereumSmartContractOracle: This handles validation of events of type
EthereumSmartContractCallEvent to process calls to execute embedded smart contracts.
StopContractOracle: This handles validation of events of type
StopExecutionEvent to stop calls to execute embedded smart contracts.
ResumeContractOracle: This handles validation of events of type
ResumeExecutionEvent to resume calls to execute embedded smart contracts which had previously been stopped with a
TemplateLoadOracle: This handles validation of events of type
LoadTemplate to load a new/altered version of a template into the VM.
OpenlawIdentityOracle: This is a trait which
OpenlawSignatureOracle extends. It contains helper functions and values such as
isSignatureValid (for checking the validity of an incoming signature) and
providerId (for storing the identity provider).
OpenlawSignatureOracle: This handles validation of events of type
SignatureEvent to process a signature on the Ethereum blockchain once the signature and Ethereum address of the signee have been verified.
In addition to the seven oracles mentioned above, the
oracles folder also contains a
CryptoService with helper functions for sha256 checksums and validating ECS signatures.
The parser stores information regarding custom types in the OpenLaw Markup Language and how they should be processed. This folder has three sub-folders:
contract folder is the simplest, containing just the
ParagraphEdits type, which is used to represent paragraphs added to templates by the user.
fields folder contains various
case objects which extend the base type
ContractField. They provide mappings between custom Scala types and field names in the
TemplateEditor for an agreement. For example, a
Name field in the editor corresponds to a custom
FullName type internally.
template folder is the most complex of the three. Definitions of features of the OpenLaw Markup Language and their expected behavior can be found here. In general, the Markup Language docs are a solid companion to keep open while perusing this part of the codebase.
For example, the
formatters folder contains all classes extending the
Formatter trait, which corresponds to the formatting features of the markup language as discussed in the documentation. The
EthereumSmartContractCall type declared in
variableTypes defines the logic for the smart contracts described in the docs. (The eagle-eyed may also notice the correspondence with the
EthereumSmartContractCallEvent we mentioned in our
EthereumSmartContractOracle above). The custom
IdentityType, also in
variableTypes, corresponds to the
Identity type which must be added to any agreement before it can be sent for signature.
Another key utility here is the
printers sub-folder. This contains case classes extending the
HTMLAgreementPrinter trait. These custom types specify various means of outputting a marked-up legal agreement in HTML, which may be useful if integrating an OpenLaw legal agreement into a custom web application.
Finally, but perhaps most crucially, the
OpenlawTemplateLanguageParserService provides many essential methods for interacting with marked-up legal agreements, including compiling templates, parsing expressions, and rendering output based on variable type.
values folder contains several other types which are key to OpenLaw's formulation of templates and contracts.
ContractAccess defines permission levels for contracts, which, when integrated elsewhere, enable granularity of access based on the use case.
ContractDefinition holds the definition of a contract and contains the following parameters:
creatorId: The creator of the contract with type
mainTemplate: The main template of the contract with type
TemplateId, analogous to the main class in a program. The
TemplateIdtype is defined elsewhere in the
templates: Other templates which may be included in the contract with type
parameters: The parameters of the contract, as input by the user in the template editor, with type
TemplateParameterstype is defined elsewhere in the
paragraphs: Paragraphs within the contract which have been edited by the user in the template editor, with type
Note that a
ContractDefinition needs the VM in order to be executed.
More broadly speaking, on the OpenLaw platform, a contract has an ID which is a hash representing the contract, based on the creator ID, ID of the main template, all the parameters, and the template scope. This hash gives significant security advantages. First, as with any hash, since its ID will change if any of the above changes, it serves as a useful, unique means of identifying the contract, as well as determining if it has been tampered with. Second, though the hash will be stored on the Ethereum blockchain, it will be difficult to derive any useful information from the hash alone. Finally, since a contract has multiple signatories, any attempts at tampering from one signatory's side will not get very far, since the versions between the two signatories have diverged, as indicated by the conflicting hashes, and thus the consensus will fail.
Functionality relating to the ID is stored in the
ContractId case class and corresponding object.