Inheritance contracts (agent side and client side) are used to pre-allocate on-chain assets to specified accounts(inheritors). Those inheitors will actually receive the assets when the inheritance conditions met and the transfer actions triggered(or mined) by third parties(the miners).
On-chain assets are protected by asymmetric encryption in which private keys play the key role. In reality, owning a private key means owning the assets linked to it. As a result, how to store the private keys securely is becoming the essential techniques in cryptocurrency world. Many methods have been introduced like: encrypted hardware wallet, cold(offline) wallet or even key-printed-paper wallet. Those ways of protection undoubtedly impove the security level by isolating the private key data from online environment. However, from another point of view, assets can become lost-prone when private keys are controlled by a single person which unfuturnately is quite common. When they are controlled by only one person, there is no way to tolerate the private keys being lost no matter by missing the wallet device, accidently damaging the storage media or even the memory killing of the human owner. Take the mysterious death of the owner of exchange QuadrigaCX, for instance. Up to $190 million owned to 115,000 customers were lost or could not be accessed because only the owner held the password to off-line cold wallets.
In stead of managing assets with private keys owned by single person, on-chains inheritance contracts can be used to sprend the private-key-lost risk by diversifying into different specified persons(accounts). The inheritance contracts will conditionally transfer the assets from the private-key-lost owner to the specified inheritors. It should be noted that those assets are still fully controlled by the original owner before the conditions met. To achieve this, the original account owner needs turn his/her account from normal account to contract account by deploying the inheritance client contract.
The inheritance contracts mechanism consists of 3 roles as the following:
-
client
The asset owner who hold the private key. This owner can deploy client contract to activate the inheritance function to specify how much assets are allocated to which accounts at what datetime.
-
agent
The inheritance action inherface. It severs 2 purposes: 1. provents miners from interracting with client contract directly; 2. records the service charge to clients and the reward earned by miners.
-
miner
Some one who triggers the client-state-change actions which will finally transfer the assets to the inheritors. Miners earn reward by calling the mining action at the very right datetime.
The following graph illustrates the relationship between the three
graph LR;
id1((miner))-- mine specified inheritance -->id2((agent))
id2((agent))-- trigger client execute -->id3((client))
id3((client))-- notify execute result -->id2((agent))
The whole process of inheritance as the following involving two contracts: client contract and agent contract
- Agent deployment
- Agent downloads and deploys the agent contract.
- Agent initializes the contract.
- Client deployment
- Client downloads and deploys the client contract.
- Client initializes the contract.
- Client enables the inheritance: enable action is a global switch to enable or disable all the inheritance allocations.
- Client allocates assets: specifies inheritor account, assets quantity, datetime, cool down duration(heistate period). Client provides a action can be used to suspend the specified inheritance individually rather than globally.
- Client deposits tokens to agent for the inheritance service. Client can withdraw the deposit before inheritance conditions being met.
- Miner deposits tokens to agent for the promise: charge will be made if the miner uncarefully calls the mining actions frequently.
- Miner mines inheritance sepcified by the client: if the mining changes the inheritance state successfully, miner will receive reward.
- CD(cool down) mining: change inheritance state from active to heistate.
- transfer mining: change inheritance state from heistate to transfered(assets will be transfered to inhertors accordingly).
cd inheritance/InheritAgent
mkdir build && cd build
cmake ..
make
cd inheritance/InheritClt
mkdir build && cd build
cmake ..
make
Assume the client account named: client, the agent account named: agent, the client contract named: InheritClt, the agent contract named: InheritAgent
cleos set contract agent InheritAgent -p agent
cleos push action agent init '[""]' -p agent
cleos set account permission agent active --add-code
cleos set contract client InheritClt -p client
cleos push action client init '[""]' -p client
cleos set account permission client active --add-code
cleos push action client setenable '[true]' -p client
-
to allocate assets
Client allocates assets ASSET AMOUNT of token contract CONTRACT NAME to account inheritor account INHERITOR at datetime DATETIME plus heistate duration CD and remark REMARK
cleos push action client allocate '["INHERITOR", "CONTRACT NAME", "ASSET AMOUNT", DATETIME, CD, "REMARK"]' -p client
-
to update or reactivate inheritance allocation
Recall above action with the same INHERITOR and CONTRACT NAME will update and activate the previous inheritance allocation
-
to cancel the allocation
Call following action with account INHERITOR, token contract CONTRACT NAME and the TOKEN SYMBOL
cleos push action client unallocate '["INHERITOR", "CONTRACT NAME", "TOKEN SYMBOL"]' -p client
-
to freeze the allocated inheritance
Call following action with account INHERITOR, token contract CONTRACT NAME and the TOKEN SYMBOL
cleos push action client freeze '["INHERITOR", "CONTRACT NAME", "TOKEN SYMBOL"]' -p client
-
to mine
Miner can mine client's inheritance by specifying the inheritor account INHERITOR, token contract CONTRACT NAME, asset amount ASSET AMOUNT, client account CLIENT and the miner himself/herself MINER. If the miner mines the inheritance successfully, he/she will get the mining reward. If the miner mines 3 times and all failed, he/she will get a fine (used to provent miners from mining frequently and waste resource).
cleos push action agent mine '["INHERITOR", "CONTRACT NAME", "ASSET AMOUNT", "CLIENT", "MINER"]' -p MINER
-
miner claims reward
Miner can call this action to claim the reward plus the desposit
cleos push action agent minerclaim '["MINER"]' -p MINER
-
client claims deposit
Client can claim the previous deposit made for the service. However, if any successful mining happened before calling this action, the service charge amount will be non-refundable.
cleos push action agent clientclaim '["CLIENT"]' -p CLIENT
-
agent claims reward
Agent can claim the reward for the inheritance service
cleos push action agent selfclaim '["RECEIVER ACCOUNT"]' -p agent
-
miner deposit
Miner deposits token to agent by transfer with remark "miner"
cleos transfer MINER agent "TOKEN AMOUNT" "miner" -p MINER
-
client deposit
Client deposits token to agent by transfer with remark "client"
cleos transfer CLIENT agent "TOKEN AMOUNT" "client" -p CLIENT
cleos set contract agent InheritAgent -p agent
Reading WASM from /projects/contracts/inheritance/InheritAgent/build/InheritAgent/InheritAgent.wasm...
Publishing contract...
executed transaction: 997eb8357fa15fa3baacc3115040610069b9e378821d521eeda0b1fbb70689c3 14408 bytes 2774 us
# eosio <= eosio::setcode {"account":"agent","vmtype":0,"vmversion":0,"code":"0061736d0100000001ed012460000060017e0060047e7e7e...
# eosio <= eosio::setabi {"account":"agent","abi":"0e656f73696f3a3a6162692f312e31010553746174650575696e74380d0a436c69656e7442...
warning: transaction executed locally, but may not be confirmed by the network yet ]
cleos push action agent init '[""]' -p agent
executed transaction: 578a5a68412902bbab924fea3c03f5c6182d0ed5827993d0db1062e1592f90ff 96 bytes 273 us
# agent <= agent::init ""
warning: transaction executed locally, but may not be confirmed by the network yet ]
cleos set account permission agent active --add-code
executed transaction: f3290972c9125ffe7d1a68ab9790cb9f14d9f4bc3ee77a9f5617f041313187eb 184 bytes 196 us
# eosio <= eosio::updateauth {"account":"agent","permission":"active","parent":"owner","auth":{"threshold":1,"keys":[{"key":"EOS8...
cleos set contract client InheritClt -p client
Reading WASM from /projects/contracts/inheritance/InheritClt/build/InheritClt/InheritClt.wasm...
Publishing contract...
executed transaction: 3908bad5450f01bf7563cbdbd1bfdcb8f6fec9545e90caf2676480d1c04b2df8 15544 bytes 8610 us
# eosio <= eosio::setcode {"account":"client","vmtype":0,"vmversion":0,"code":"0061736d0100000001fd012660000060017e0060047e7e7...
# eosio <= eosio::setabi {"account":"client","abi":"0e656f73696f3a3a6162692f312e31010553746174650575696e74380e0a416c6c6f63617...
warning: transaction executed locally, but may not be confirmed by the network yet ]
cleos push action client init '[""]' -p client
executed transaction: 9a9aad2bd97f3a4604bd2a1ac763c38b3d6e871e9cfa0842f10613227a3ec8d0 96 bytes 230 us
# client <= client::init ""
warning: transaction executed locally, but may not be confirmed by the network yet ]
cleos set account permission client active --add-code
executed transaction: 1c8c2c6f94514a028477f340eb17e1102425ea0f3f63429ecb45b04494f1f193 184 bytes 181 us
# eosio <= eosio::updateauth {"account":"client","permission":"active","parent":"owner","auth":{"threshold":1,"keys":[{"key":"EOS...
warning: transaction executed locally, but may not be confirmed by the network yet
cleos push action client setenable '[true]' -p client
executed transaction: 10c7dc22b2f333e604106cd0628418b5cffb2415d5707308aad03bd7387dc683 96 bytes 245 us
# client <= client::setenable {"enabled":1}
>> [InheritClt::setenable] enabled
warning: transaction executed locally, but may not be confirmed by the network yet ]
cleos push action client allocate '["inheritor1", "eosio.token", "10.0000 SYS", 1578392977, 60, "inheritance test: allocate 10 SYS to inheritor1"]' -p client
executed transaction: ebbd77941e567fd551474ca2ae1f55bfd688f5192de5c5f3e96f3039d876a4e9 184 bytes 456 us
# client <= client::allocate {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","validFrom":1578392...
>> [InheritClt::allocate] allocated : 10.0000 SYS, unallocated: 90.0000 SYS, transfered: 0.0000 SYS
warning: transaction executed locally, but may not be confirmed by the network yet ]
cleos push action client allocate '["inheritor2", "eosio.token", "20.0000 SYS", 1578392977, 30, "inheritance test: allocate 20 SYS to inheritor2"]' -p client
executed transaction: 7e3dae04c01a1e704988cdb38f6feffae8036e78fbf190800884c3d8a41b8f6b 184 bytes 497 us
# client <= client::allocate {"inheritor":"inheritor2","tokencontract":"eosio.token","quantity":"20.0000 SYS","validFrom":1578392...
>> [InheritClt::allocate] allocated : 30.0000 SYS, unallocated: 70.0000 SYS, transfered: 0.0000 SYS
warning: transaction executed locally, but may not be confirmed by the network yet
A miner can deposit into (transfer to) agent with comment "miner". A client can deposit with comment "client". Any transfer to agent token with remark other than "miner" or "client" will be refunded immediately.
cleos transfer miner1 agent "0.1000 SYS" "miner" -p miner1
executed transaction: a39da0f87006f701d7236e64e87875d444f994fd676d17ef661992e6db6f66e4 136 bytes 477 us
# eosio.token <= eosio.token::transfer {"from":"miner1","to":"agent","quantity":"0.1000 SYS","memo":"miner"}
# miner1 <= eosio.token::transfer {"from":"miner1","to":"agent","quantity":"0.1000 SYS","memo":"miner"}
# agent <= eosio.token::transfer {"from":"miner1","to":"agent","quantity":"0.1000 SYS","memo":"miner"}
>> [InheritAgent::ondeposit] receive miner deposit from miner1, quantity: 0.1000 SYS, memo: miner
warning: transaction executed locally, but may not be confirmed by the network yet ]
cleos transfer client agent "5.0000 SYS" "client" -p client
executed transaction: ff9c942565397da80957e584a7e6c04120506e7befa7eba486a39f87dd32f6d8 136 bytes 489 us
# eosio.token <= eosio.token::transfer {"from":"client","to":"agent","quantity":"5.0000 SYS","memo":"miner"}
# client <= eosio.token::transfer {"from":"client","to":"agent","quantity":"5.0000 SYS","memo":"miner"}
# agent <= eosio.token::transfer {"from":"client","to":"agent","quantity":"5.0000 SYS","memo":"miner"}
>> [InheritAgent::ondeposit] receive miner deposit from client, quantity: 5.0000 SYS, memo: miner
warning: transaction executed locally, but may not be confirmed by the network yet ]
cleos push action agent mine '["inheritor1", "eosio.token", "10.0000 SYS", "client", "miner1"]' -p miner1
If the CD mining condition is met, the following result will be printed out
executed transaction: e1b0f3b5b4217eb306f7cdd80d4589273c0435c86279d2afb0647bd05b6edb69 144 bytes 8266 us
# agent <= agent::mine {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","assetclient":"clie...
>> [InheritAgent::mine] call onagentmine action first receiver: agent; inheritor: inheritor1, token contract: eosio.token, quantity: 10.0000 SYS
# client <= client::onagentmine {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","assetclient":"clie...
>> [InheritClt::onagentmine] done CD mining, inheritor: inheritor1, token contract: eosio.token, quantity: 10.0000 SYS, cdBeganTime: 1578542493
# agent <= client::onagentmine {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","assetclient":"clie...
warning: transaction executed locally, but may not be confirmed by the network yet ]
If the miner mines repeatedly like calling above "mine" action again will receive following result
executed transaction: fe264b70252fad0194e20cd86f1a1a24a57be05456de4c98feb006e27cf5c5e2 144 bytes 534 us
# agent <= agent::mine {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","assetclient":"clie...
>> [InheritAgent::mine] call onagentmine action first receiver: agent; inheritor: inheritor1, token contract: eosio.token, quantity: 10.0000 SYS
# client <= client::onagentmine {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","assetclient":"clie...
>> [InheritClt::onagentmine] repeated CD mining invalid
warning: transaction executed locally, but may not be confirmed by the network yet ]
if the transfer mining condition is met (only possible after CD mining done), calling above actioin will get the following that shows the client's specified inheritance assets are transfered to the inheritor accordingly
executed transaction: 9a591c3b9c8ba1a9e4dc6ee08f684e7da42d989def28fa14212da12d3fb01d76 144 bytes 1316 us
# agent <= agent::mine {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","assetclient":"clie...
>> [InheritAgent::mine] call onagentmine action first receiver: agent; inheritor: inheritor1, token contract: eosio.token, quantity: 10.0000 SYS
# client <= client::onagentmine {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","assetclient":"clie...
>> [InheritClt::onagentmine] done Transfer mining, inheritor: inheritor1, token contract: eosio.token, quantity: 10.0000 SYS, transTime: 1578543215
# agent <= client::onagentmine {"inheritor":"inheritor1","tokencontract":"eosio.token","quantity":"10.0000 SYS","assetclient":"clie...
# eosio.token <= eosio.token::transfer {"from":"client","to":"inheritor1","quantity":"10.0000 SYS","memo":"inheritance test: allocate 10 SY...
# client <= eosio.token::transfer {"from":"client","to":"inheritor1","quantity":"10.0000 SYS","memo":"inheritance test: allocate 10 SY...
# inheritor1 <= eosio.token::transfer {"from":"client","to":"inheritor1","quantity":"10.0000 SYS","memo":"inheritance test: allocate 10 SY...
warning: transaction executed locally, but may not be confirmed by the network yet ]
- Note: for every inheritance allocation record being served, the client need to pay the service respectivelly. The client can deposit enough tokens for all the allocations to process or do that separately as long as the mining conditions unmet and no mining occurs. Otherwise error message will be presented during the mining process which cause the inheritance service stopped.
call the following action will claim the assets including previous deposit and mining reward
cleos push action agent minerclaim '["miner1"]' -p miner1
This example shows miner1 has been rewarded 2.0000 SYS (for the previous CD mining and transfer mining). This miner received 2.1000 SYS in total including the 0.1000 SYS deposit
executed transaction: 714f89c5b36da72b0ec079b2deadf0c81fed2867ca2b8f7fa6b023db00449b48 104 bytes 728 us
# agent <= agent::minerclaim {"miner":"miner1"}
>> [InheritAgent::minerclaim] reward: 2.0000 SYS, deposit refund: 0.1000 SYS
# eosio.token <= eosio.token::transfer {"from":"agent","to":"miner1","quantity":"2.1000 SYS","memo":"reward: 2.0000 SYS, deposit refund: 0....
# agent <= eosio.token::transfer {"from":"agent","to":"miner1","quantity":"2.1000 SYS","memo":"reward: 2.0000 SYS, deposit refund: 0....
# miner1 <= eosio.token::transfer {"from":"agent","to":"miner1","quantity":"2.1000 SYS","memo":"reward: 2.0000 SYS, deposit refund: 0....
warning: transaction executed locally, but may not be confirmed by the network yet ]
call the following action will refund any token balance of client left in agent
cleos push action agent clientclaim '["client"]' -p client
assume there are 2.0000 SYS tokens left in agent, call above action will receive the following printout
executed transaction: df99fef744c320a5981d3358e99309624931f7fb30468d43ba77f6c4a85ecf3f 104 bytes 593 us
# agent <= agent::clientclaim {"client":"client"}
>> [InheritAgent::clientclaim] deposit refund: 2.0000 SYS
# eosio.token <= eosio.token::transfer {"from":"agent","to":"client","quantity":"2.0000 SYS","memo":"deposit refund: 2.0000 SYS"}
# agent <= eosio.token::transfer {"from":"agent","to":"client","quantity":"2.0000 SYS","memo":"deposit refund: 2.0000 SYS"}
# client <= eosio.token::transfer {"from":"agent","to":"client","quantity":"2.0000 SYS","memo":"deposit refund: 2.0000 SYS"}
warning: transaction executed locally, but may not be confirmed by the network yet ]