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 totrue
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 totrue
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 totrue
as long as the document has enough revoke request from operators.
signed_date
is the UNIX timestamp (result ofblock.timestamp
in solidity) of the block where signature has been officially signed.revoked_date
is the UNIX timestamp (result ofblock.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 withparam: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 {}