ULCDocKernel

Summary

Each organisation or individual has to publish his own ULCDocKernel smart contract. The kernel part is used to explicitly publish signatures to blockchain. So, the owner of ULCDocKernel has a complete and independent way to certify documents.

The kernel has different features :

  • Multi-Owner ability
  • Native timestamp tracking
  • Undestroyable signatures

Multi-Owner and Multi-Operator ability

For most of important organization, only one person can’t do dangerous actions alone, such as signing an official document. In this way, the kernel has the ability to wait for multiple confirmations before doing important stuff, like publishing a signature, or changing critical configuration parameters.

Roles

ULCDocKernel has 2 floors of administrative process :

  • owners that are administrators of the Smart Contract. They can change sensible parameters.
  • operators that can only push, confirm and revoke signatures.

Note

Owners have operators rights

In order to do something on the Smart Contract, like signing or changing parameters, you need to call every time a requester that will record you request and do it if it reaches operatorsForChange and respectively ownersForChange request count.

Warning

An account can’t be owner and operator at the same time.

By default, ULCDocKernel is configurated to work with one owner account. So, is you use the kernel with only one owner, the request part is totally transparent and then they are done immediately.

How the requester works

When you want to do an action, all requesters will create a keccak256 hash of the request and store it inside a mapping.

mapping(bytes32 => address[]) public doOwnerRequest;
mapping(bytes32 => address[]) public doOperatorRequest;

Then, each address who request the same thing will be added into an array and when it’s length reaches operatorsForChange or ownersForChange, the action requested is done.

Constructor

constructor() public {
    owners[msg.sender] = true;
    ownerCount = 1;
    ownersForChange = 1;
    operatorsForChange = 1;
}

By default, the creator of the smart contract is the owner and everything is configurated to work with only one confirmation.

Variables available

uint256 public ownerCount;
uint256 public ownersForChange;

uint256 public operatorCount;
uint256 public operatorsForChange;

mapping (address => bool) public owners;
mapping(bytes32 => address[]) public doOwnerRequest;

mapping (address => bool) public operators;
mapping(bytes32 => address[]) public doOperatorRequest;

event OwnershipNewInteractor(address indexed newOwner);

event OwnershipLossInteractor(address indexed oldOwner);

event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
);

event OperatorNewInteractor(address indexed newOperator);

event OperatorLossInteractor(address indexed oldOperator);

event OperatorshipTransferred(
    address indexed previousOperator,
    address indexed newOperator
);

Functions available

Basic setters

//Both need enough confirmations (they are requester too)
function  setOperatorsForChange(uint256 _nb) external onlyOwner {}

function setOwnersForChange(uint256 _nb) external onlyOwner {}

Requester

function requestAddOwner(address _newOwner) external onlyOwner{}

function requestAddOperator(address _newOperator) external onlyOwner {}

function requestChangeOwner(address _oldOwner, address _newOwner) external onlyOwner{}

function requestRemoveOwner(address _removableOwner) external onlyOwner{}

function requestRemoveOperator(address _removableOperator) external onlyOwner{}

Getters

//Returns all adresses who approved the keccak256 operator request
function getOperatorRequest(bytes32 _theKey) external view returns(address[] memory) {}

//Returns numbers of operators who confirmed the keccak256 request.
function getOperatorRequestLength(bytes32 _theKey) external view returns(uint256) {}

//Returns all adresses who approved the keccak256 owner request
function getOwnerRequest(bytes32 _theKey) external view returns(address[] memory) {}

//Returns numbers of owners who confirmed the keccak256 request.
function getOwnerRequestLength(bytes32 _theKey) external view returns(uint256) {}

How signatures are stored

Signature Structure

ULCDocKernel has a struct called Document which has all information about a signed document.

struct Document {
      bool initialized;
      bool signed;
      bool revoked;

      uint256 signed_date;
      uint256 revoked_date;
      uint16 document_family;

      string revoked_reason;
      string source;
      string extra_data;
}

By default, the EVM makes all var set to false, 0, or "".

  • initialized is set to true as soon as someone started to try signing a document. When it is activated, you can’t push an another version of the document. It’s a security to prevent deleting, cheating about the fact that you sign some extra data, document family and so on.
  • signed is set to true as long as the document has enough confirmations. It’s the only field you need to trust to know if something has been signed or not.
  • revoked is set to true as long as the document has enough revoke request from operators.
  • signed_date is the UNIX timestamp (result of block.timestamp in solidity) of the block where signature has been officially signed.
  • revoked_date is the UNIX timestamp (result of block.timestamp in solidity) of the block where revoked state has been officially declared.
  • document_family is the index of the array where is stored the string value.
  • revoked_reason is defined by operators when they push a revoke statement.
  • source is defined by operators when they push document. It is the location where we can find the document if it’s public.
  • extra_data is defined by operators when they push document. It is a free location with param:value,param2:value2. It can be used to add extra information for automatic process, or to be compatible with newer Kernel Version.

Find a signature

mapping(bytes32 => Document) public Signatures_Book;

To find a signature, you need its bytes32 code. To obtain it, just check the Hash_Algorithm string. By default, ULCDocKernel uses SHA3-256 hash of the document.

Note

Because mapping hashes its key with a 32 bytes format, it is useless to use hash algorithm with more than 32 bytes output like SHA3-512.

Constructor

constructor() public {
    Hash_Algorithm = "SHA3-256";
}

Variables available

uint256 public Document_Counter; // stat purposes only
string public Hash_Algorithm; //Essential Information to know how to hash files

string[] public document_family_registred = ["Undefined",
"Diploma",
"Bill",
"Order",
"Letter",
"Publication",
"Code",
"Image",
"Audio",
"Video",
"Binary"];

mapping(bytes32 => Document) public Signatures_Book;

Function List

Pushing something is the first step to do sign a document with data. Then, you need to confirm it before changing sign state to true.

function pushDocument(bytes32 _SignatureHash, string memory _source, uint16 _indexDocumentFamily, string memory _extra_data) public atLeastOperator whenNotPaused notUpgraded{}

Note

When you push a document into your Kernel, you automatically confirm it. So, if you use a simple signature Kernel, your document is signed with only one transaction.

//Request to confirm a signature. It can also be used to simply sign document without extra_data.
function confirmDocument(bytes32 _SignatureHash) public atLeastOperator whenNotPaused notUpgraded{}

//Request to add a "revoked" statement on the signature, and add a reason for that (can be then displayed on clients).
function pushRevokeDocument(bytes32 _SignatureHash, string calldata _reason) external atLeastOperator whenNotPaused {}

//Request to confirm a revoke statement. It can also be used to simply revoke document without reason
function confirmRevokeDocument(bytes32 _SignatureHash) external atLeastOperator whenNotPaused {}