Merge 27-ens-usernames-idopt2 with 27-ens-usernames

This commit is contained in:
Richard Ramos 2018-03-15 14:15:37 -04:00
commit 807e692829
12 changed files with 1270 additions and 286 deletions

View File

@ -0,0 +1,63 @@
pragma solidity ^0.4.17;
import "./DelayedUpdatableInstanceStorage.sol";
import "./DelegatedCall.sol";
/**
* @title DelayedUpdatableInstance
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
* @dev Contract that can be updated by a call from itself.
*/
contract DelayedUpdatableInstance is DelayedUpdatableInstanceStorage, DelegatedCall {
event UpdateRequested(address newKernel, uint256 activation);
event UpdateCancelled();
event UpdateConfirmed(address oldKernel, address newKernel);
function DelayedUpdatableInstance(address _kernel) public {
kernel = _kernel;
}
/**
* @dev delegatecall everything (but declared functions) to `_target()`
* @notice Verify `kernel()` code to predict behavior
*/
function () external delegated {
//all goes to kernel
}
/**
* @dev returns kernel if kernel that is configured
* @return kernel address
*/
function targetDelegatedCall()
internal
constant
returns(address)
{
return kernel;
}
function updateRequestUpdatableInstance(address _kernel) external {
require(msg.sender == address(this));
uint activation = block.timestamp + 30 days;
update = Update(_kernel, activation);
UpdateRequested(_kernel, activation);
}
function updateConfirmUpdatableInstance(address _kernel) external {
require(msg.sender == address(this));
Update memory pending = update;
require(pending.kernel == _kernel);
require(pending.activation < block.timestamp);
kernel = pending.kernel;
delete update;
UpdateConfirmed(kernel, pending.kernel);
}
function updateCancelUpdatableInstance() external {
require(msg.sender == address(this));
delete update;
}
}

View File

@ -0,0 +1,21 @@
pragma solidity ^0.4.17;
import "./InstanceStorage.sol";
/**
* @title InstanceStorage
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
* @dev Defines kernel vars that Kernel contract share with Instance.
* Important to avoid overwriting wrong storage pointers is that
* InstanceStorage should be always the first contract at heritance.
*/
contract DelayedUpdatableInstanceStorage is InstanceStorage {
// protected zone start (InstanceStorage vars)
Update update;
struct Update {
address kernel;
uint256 activation;
}
// protected zone end
}

View File

@ -23,7 +23,7 @@ contract ERC725 {
function getKeyPurpose(bytes32 _key) public constant returns(uint256[] purpose);
function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys);
function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success);
function removeKey(bytes32 _key, uint256 _purpose) returns (bool success);
function removeKey(bytes32 _key, uint256 _purpose) public returns (bool success);
function execute(address _to, uint256 _value, bytes _data) public returns (uint256 executionId);
function approve(uint256 _id, bool _approve) public returns (bool success);
}

View File

@ -12,62 +12,117 @@ contract Identity is ERC725, ERC735 {
mapping (uint256 => bytes32[]) claimsByType;
mapping (bytes32 => uint256) indexes;
mapping (uint => Transaction) txx;
mapping (uint => Action) actionCatalogByTrx;
mapping (uint => bytes32) claimCatalogByTrx;
mapping (uint256 => uint8) minimumApprovalsByKeyType;
mapping (bytes32 => Claim) pendingClaims;
mapping (uint256 => uint256) minimumApprovalsByKeyPurpose;
bytes32[] pendingTransactions;
uint nonce = 0;
address recoveryContract;
address recoveryManager;
struct Action {
struct Transaction {
address to;
uint value;
bytes data;
}
uint8 constant ACTION_TRANSACTION = 1;
uint8 constant CLAIM_TRANSACTION = 2;
struct Transaction {
uint8 trxType;
uint nonce;
uint approverCount;
mapping(uint256 => uint8) approvalsByKeyType;
mapping(bytes32 => bool) approvals;
bool executed;
}
modifier managerOnly {
require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY));
require(
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
);
_;
}
modifier selfOnly {
require(msg.sender == address(this));
require(
msg.sender == address(this)
);
_;
}
modifier actorOnly {
require(isKeyType(bytes32(msg.sender), ACTION_KEY));
modifier recoveryOnly {
require(
(recoveryContract != address(0) && msg.sender == address(recoveryContract))
);
_;
}
modifier claimSignerOnly {
require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
modifier actorOnly(bytes32 _key) {
require(isKeyType(_key, ACTION_KEY));
_;
}
modifier managerOrActor {
modifier managerOrActor(bytes32 _key) {
require(
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
isKeyType(bytes32(msg.sender), ACTION_KEY)
);
_;
}
modifier validECDSAKey (
bytes32 _key,
bytes32 _signHash,
uint8 _v,
bytes32 _r,
bytes32 _s
)
{
require(address(_key) == ecrecover(
keccak256("\x19Ethereum Signed Message:\n32", _signHash),
_v,
_r,
_s
));
require(keys[_key].purpose != 0);
_;
}
function Identity() public {
_addKey(bytes32(msg.sender), MANAGEMENT_KEY, 1);
minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
_constructIdentity(msg.sender);
}
function ()
public
payable
{
}
function managerReset(address _newKey)
public
recoveryOnly
{
recoveryManager = _newKey;
_addKey(bytes32(recoveryManager), MANAGEMENT_KEY, 0);
minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = keysByPurpose[MANAGEMENT_KEY].length;
}
function processManagerReset(uint256 limit)
public
{
require(recoveryManager != address(0));
bytes32 newKey = bytes32(recoveryManager);
bytes32[] memory managers = keysByPurpose[MANAGEMENT_KEY];
uint256 totalManagers = managers.length;
if (limit == 0) {
limit = totalManagers;
}
minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = totalManagers - limit + 1;
for (uint256 i = 0; i < limit; i++) {
bytes32 manager = managers[i];
if (manager != newKey) {
_removeKey(manager, MANAGEMENT_KEY);
totalManagers--;
}
}
if (totalManagers == 1) {
recoveryManager = address(0);
}
}
function addKey(
@ -78,11 +133,26 @@ contract Identity is ERC725, ERC735 {
public
selfOnly
returns (bool success)
{
{
_addKey(_key, _purpose, _type);
return true;
}
function replaceKey(
bytes32 _oldKey,
bytes32 _newKey,
uint256 _newType
)
public
selfOnly
returns (bool success)
{
uint256 purpose = keys[_oldKey].purpose;
_addKey(_newKey, purpose, _newType);
_removeKey(_oldKey, purpose);
return true;
}
function removeKey(
bytes32 _key,
uint256 _purpose
@ -101,118 +171,34 @@ contract Identity is ERC725, ERC735 {
bytes _data
)
public
managerOrActor
managerOrActor(bytes32(msg.sender))
returns (uint256 executionId)
{
executionId = createTransaction(ACTION_TRANSACTION);
ExecutionRequested(executionId, _to, _value, _data);
actionCatalogByTrx[nonce] = Action({to: _to, value: _value, data: _data});
executionId = _execute(_to, _value, _data);
approve(executionId, true);
}
function approve(uint256 _id, bool _approve)
function approve(uint256 _id, bool _approval)
public
managerOrActor
managerOrActor(bytes32(msg.sender))
returns (bool success)
{
Transaction storage trx = txx[_id];
require(trx.executed == false);
bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY);
bytes32 actorKeyHash = keccak256(bytes32(msg.sender), ACTION_KEY);
uint8 approvalCount;
uint256 requiredKeyType;
if (trx.trxType == ACTION_KEY) {
Action memory action = actionCatalogByTrx[_id];
if (action.to == address(this)) {
requiredKeyType = MANAGEMENT_KEY;
if (keys[managerKeyHash].purpose == MANAGEMENT_KEY)
approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
} else {
requiredKeyType = ACTION_KEY;
if (keys[managerKeyHash].purpose == ACTION_KEY)
approvalCount = _calculateApprovals(actorKeyHash, ACTION_KEY, _approve, trx);
}
if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) {
trx.executed = true;
Executed(_id, action.to, action.value, action.data);
success = action.to.call.value(action.value)(action.data);
}
} else {
// It is a claim
bytes32 claimHash = claimCatalogByTrx[_id];
if (keys[managerKeyHash].purpose == MANAGEMENT_KEY)
approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
if (approvalCount >= minimumApprovalsByKeyType[MANAGEMENT_KEY]) {
Claim memory c = pendingClaims[claimHash];
claims[claimHash] = Claim(
{
claimType: c.claimType,
scheme: c.scheme,
issuer: c.issuer,
signature: c.signature,
data: c.data,
uri: c.uri
});
indexes[claimHash] = claimsByType[c.claimType].length;
claimsByType[c.claimType].push(claimHash);
ClaimAdded(claimHash, c.claimType, c.scheme, c.issuer, c.signature, c.data, c.uri);
trx.executed = true;
}
}
return _approve(bytes32(msg.sender), _id, _approval);
}
function setMiminumApprovalsByKeyType(
uint256 _type,
uint8 _minimumApprovals
function setMinimumApprovalsByKeyType(
uint256 _purpose,
uint256 _minimumApprovals
)
public
selfOnly
{
minimumApprovalsByKeyType[_type] = _minimumApprovals;
}
function _calculateApprovals(
bytes32 _keyHash,
uint256 _keyType,
bool _approve,
Transaction storage trx
)
private
returns (uint8 approvalCount)
{
if (trx.approvals[_keyHash] == false && _approve) {
trx.approvalsByKeyType[_keyType]++;
} else if (trx.approvals[_keyHash] == true && !_approve && trx.approverCount > 0) {
trx.approvalsByKeyType[_keyType]--;
}
trx.approvals[_keyHash] = _approve;
trx.approverCount++;
return trx.approvalsByKeyType[_keyType];
require(_minimumApprovals > 0);
require(_minimumApprovals <= keysByPurpose[_purpose].length);
minimumApprovalsByKeyPurpose[_purpose] = _minimumApprovals;
}
function createTransaction(uint8 _trxType) private returns (uint256 id){
id = nonce;
txx[nonce] = Transaction(
{
nonce: nonce,
trxType: _trxType,
approverCount: 0,
executed: false
});
nonce++;
}
function getTransactionIdByClaim(bytes32 _claimHash) public view returns (uint256 id) {
return indexes[_claimHash];
}
function addClaim(
uint256 _claimType,
uint256 _scheme,
@ -222,60 +208,45 @@ contract Identity is ERC725, ERC735 {
string _uri
)
public
claimSignerOnly
returns (bytes32 claimRequestId)
returns (bytes32 claimHash)
{
bytes32 claimHash = keccak256(_issuer, _claimType);
claimRequestId = claimHash;
uint256 executionId = createTransaction(CLAIM_TRANSACTION);
claimCatalogByTrx[executionId] = claimHash;
indexes[claimHash] = executionId;
if (claims[claimHash].claimType > 0) {
// Claim existed, needs approval
ClaimChanged(
claimRequestId,
_claimType,
_scheme,
_issuer,
_signature,
_data,
_uri);
} else {
ClaimRequested(
claimRequestId,
_claimType,
_scheme,
_issuer,
_signature,
_data,
_uri);
}
pendingClaims[claimHash] = Claim(
{
claimType: _claimType,
scheme: _scheme,
issuer: _issuer,
signature: _signature,
data: _data,
uri: _uri
claimHash = keccak256(_issuer, _claimType);
if (msg.sender == address(this)) {
if (claims[claimHash].claimType > 0) {
_modifyClaim(claimHash, _claimType, _scheme, _issuer, _signature, _data, _uri);
} else {
_includeClaim(claimHash, _claimType, _scheme, _issuer, _signature, _data, _uri);
}
);
} else {
require(_issuer == msg.sender);
require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
_execute(address(this), 0, msg.data);
ClaimRequested(
claimHash,
_claimType,
_scheme,
_issuer,
_signature,
_data,
_uri
);
}
}
function removeClaim(bytes32 _claimId) public returns (bool success) {
function removeClaim(bytes32 _claimId)
public
returns (bool success)
{
Claim memory c = claims[_claimId];
require(
msg.sender == c.issuer ||
msg.sender == address(this) ||
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
msg.sender == address(this)
);
// MUST only be done by the issuer of the claim, or KEYS OF PURPOSE 1, or the identity itself.
// TODO If its the identity itself, the approval process will determine its approval.
uint claimIdTypePos = indexes[_claimId];
delete indexes[_claimId];
bytes32[] storage claimsTypeArr = claimsByType[c.claimType];
@ -287,43 +258,6 @@ contract Identity is ERC725, ERC735 {
return true;
}
function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) private {
bytes32 keyHash = keccak256(_key, _purpose);
require(keys[keyHash].purpose == 0);
require(
_purpose == MANAGEMENT_KEY ||
_purpose == ACTION_KEY ||
_purpose == CLAIM_SIGNER_KEY ||
_purpose == ENCRYPTION_KEY
);
KeyAdded(_key, _purpose, _type);
keys[keyHash] = Key(_purpose, _type, _key);
indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
}
function _removeKey(bytes32 _key, uint256 _purpose) private {
bytes32 keyHash = keccak256(_key, _purpose);
Key storage myKey = keys[keyHash];
KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
uint index = indexes[keyHash];
delete indexes[keyHash];
bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
keysByPurpose[_purpose][index] = replacer;
indexes[keccak256(replacer, _purpose)] = index;
keysByPurpose[_purpose].length--;
if (_purpose == MANAGEMENT_KEY) {
require(keysByPurpose[MANAGEMENT_KEY].length >= 1);
}
delete keys[keyHash];
// MUST only be done by keys of purpose 1, or the identity itself.
// TODO If its the identity itself, the approval process will determine its approval.
}
function getKey(
bytes32 _key,
uint256 _purpose
@ -384,7 +318,7 @@ contract Identity is ERC725, ERC735 {
function getKeysByPurpose(uint256 _purpose)
public
constant
returns(bytes32[] keys)
returns(bytes32[])
{
return keysByPurpose[_purpose];
}
@ -392,7 +326,14 @@ contract Identity is ERC725, ERC735 {
function getClaim(bytes32 _claimId)
public
constant
returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri)
returns(
uint256 claimType,
uint256 scheme,
address issuer,
bytes signature,
bytes data,
string uri
)
{
Claim memory _claim = claims[_claimId];
return (_claim.claimType, _claim.scheme, _claim.issuer, _claim.signature, _claim.data, _claim.uri);
@ -405,6 +346,276 @@ contract Identity is ERC725, ERC735 {
{
return claimsByType[_claimType];
}
function approveECDSA(
uint256 _id,
bool _approval,
bytes32 _key,
uint8 _v,
bytes32 _r,
bytes32 _s
)
public
validECDSAKey(
_key,
keccak256(
address(this),
bytes4(keccak256("approve(uint256,bool)")),
_id,
_approval
),
_v,
_r,
_s
)
managerOrActor(_key)
returns (bool success)
{
return _approve(_key, _id, _approval);
}
function executeECDSA(
address _to,
uint256 _value,
bytes _data,
uint _nonce,
bytes32 _key,
uint8 _v,
bytes32 _r,
bytes32 _s
)
public
validECDSAKey(
_key,
keccak256(
address(this),
bytes4(
keccak256("execute(address,uint256,bytes)")),
_to,
_value,
_data,
_nonce
),
_v,
_r,
_s
)
managerOrActor(_key)
returns (uint256 executionId)
{
executionId = _execute(_to, _value, _data);
_approve(_key, executionId, true);
}
function setupRecovery(address _recoveryContract)
public
selfOnly
{
require(recoveryContract == address(0));
recoveryContract = _recoveryContract;
}
function _constructIdentity(address _manager)
internal
{
require(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] == 0);
_addKey(bytes32(_manager), MANAGEMENT_KEY, 0);
minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = 1;
minimumApprovalsByKeyPurpose[ACTION_KEY] = 1;
}
function _execute(
address _to,
uint256 _value,
bytes _data
)
private
returns (uint256 executionId)
{
executionId = nonce;
ExecutionRequested(executionId, _to, _value, _data);
txx[executionId] = Transaction(
{
to: _to,
value: _value,
data: _data,
nonce: nonce,
approverCount: 0
});
nonce++;
}
function _approve(
bytes32 _key,
uint256 _id,
bool _approval
)
private
returns(bool success)
{
Transaction storage trx = txx[_id];
uint256 approvalCount;
uint256 requiredKeyPurpose;
Approved(_id, _approval);
if (trx.to == address(this)) {
require(isKeyType(_key, MANAGEMENT_KEY));
bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
requiredKeyPurpose = MANAGEMENT_KEY;
approvalCount = _calculateApprovals(managerKeyHash, _approval, trx);
} else {
require(isKeyType(_key, ACTION_KEY));
bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
requiredKeyPurpose = ACTION_KEY;
approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
}
if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
Executed(_id, trx.to, trx.value, trx.data);
success = trx.to.call.value(trx.value)(trx.data);
}
}
function _addKey(
bytes32 _key,
uint256 _purpose,
uint256 _type
)
private
{
bytes32 keyHash = keccak256(_key, _purpose);
require(keys[keyHash].purpose == 0);
require(
_purpose == MANAGEMENT_KEY ||
_purpose == ACTION_KEY ||
_purpose == CLAIM_SIGNER_KEY ||
_purpose == ENCRYPTION_KEY
);
KeyAdded(_key, _purpose, _type);
keys[keyHash] = Key(_purpose, _type, _key);
indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
}
function _removeKey(
bytes32 _key,
uint256 _purpose
)
private
{
bytes32 keyHash = keccak256(_key, _purpose);
Key storage myKey = keys[keyHash];
KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
uint index = indexes[keyHash];
delete indexes[keyHash];
bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
keysByPurpose[_purpose][index] = replacer;
indexes[keccak256(replacer, _purpose)] = index;
keysByPurpose[_purpose].length--;
if (_purpose == MANAGEMENT_KEY) {
require(
keysByPurpose[MANAGEMENT_KEY].length >= 1 &&
keysByPurpose[MANAGEMENT_KEY].length >= minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]
);
}
delete keys[keyHash];
}
function _calculateApprovals(
bytes32 _keyHash,
bool _approval,
Transaction storage trx
)
private
returns (uint256 approvalCount)
{
require(trx.approvals[_keyHash] != _approval);
trx.approvals[_keyHash] = _approval;
if (_approval) {
trx.approverCount++;
} else {
trx.approverCount--;
}
return trx.approverCount;
}
function _includeClaim(
bytes32 _claimHash,
uint256 _claimType,
uint256 _scheme,
address _issuer,
bytes _signature,
bytes _data,
string _uri
)
private
{
claims[_claimHash] = Claim(
{
claimType: _claimType,
scheme: _scheme,
issuer: _issuer,
signature: _signature,
data: _data,
uri: _uri
}
);
indexes[_claimHash] = claimsByType[_claimType].length;
claimsByType[_claimType].push(_claimHash);
ClaimAdded(
_claimHash,
_claimType,
_scheme,
_issuer,
_signature,
_data,
_uri
);
}
function _modifyClaim(
bytes32 _claimHash,
uint256 _claimType,
uint256 _scheme,
address _issuer,
bytes _signature,
bytes _data,
string _uri
)
private
{
require(msg.sender == _issuer);
ClaimChanged(
_claimHash,
_claimType,
_scheme,
_issuer,
_signature,
_data,
_uri
);
claims[_claimHash] = Claim({
claimType: _claimType,
scheme: _scheme,
issuer: _issuer,
signature: _signature,
data: _data,
uri: _uri
});
}
}

View File

@ -0,0 +1,32 @@
pragma solidity ^0.4.17;
import "../deploy/Factory.sol";
import "../deploy/DelayedUpdatableInstance.sol";
import "./IdentityKernel.sol";
contract IdentityFactory is Factory {
event IdentityCreated(address instance);
function IdentityFactory(bytes _infohash)
public
Factory(new IdentityKernel(), _infohash)
{
}
function createIdentity()
external
{
createIdentity(msg.sender);
}
function createIdentity(address _idOwner)
public
{
IdentityKernel instance = IdentityKernel(new DelayedUpdatableInstance(address(latestKernel)));
instance.initIdentity(_idOwner);
IdentityCreated(address(instance));
}
}

View File

@ -0,0 +1,11 @@
pragma solidity ^0.4.17;
import "../deploy/DelayedUpdatableInstanceStorage.sol";
import "./Identity.sol";
contract IdentityKernel is DelayedUpdatableInstanceStorage, Identity {
function initIdentity(address _caller) external {
_constructIdentity(_caller);
}
}

View File

@ -0,0 +1,34 @@
pragma solidity ^0.4.17;
contract TestContract {
event TestFunctionExecuted();
function test() public {
TestFunctionExecuted();
}
/*
Helper function to be used in unit testing due to error in web3
web3.utils.soliditySha3([1, 2, 3])
Error: Autodetection of array types is not supported.
at _processSoliditySha3Args (node_modules/web3-utils/src/soliditySha3.js:176:15)
*/
function hash(
address identity,
bytes32 _revealedSecret,
address _dest,
bytes _data,
bytes32 _newSecret,
bytes32[] _newFriendsHashes)
external
pure
returns(bytes32)
{
return keccak256(identity, _revealedSecret, _dest, _data, _newSecret, _newFriendsHashes);
}
}

View File

@ -0,0 +1,13 @@
pragma solidity ^0.4.17;
import "../identity/IdentityKernel.sol";
contract UpdatedIdentityKernel is IdentityKernel {
event TestFunctionExecuted(uint256 minApprovalsByManagementKeys);
function test() public {
TestFunctionExecuted(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]);
}
}

102
test/factory.js Normal file
View File

@ -0,0 +1,102 @@
const TestUtils = require("../utils/testUtils.js")
const idUtils = require("../utils/identityUtils.js")
const web3EthAbi = require("web3-eth-abi");
const Identity = artifacts.require("./identity/Identity.sol");
const IdentityFactory = artifacts.require("./identity/IdentityFactory.sol");
const UpdatableInstance = artifacts.require('./deploy/UpdatableInstance.sol');
const UpdatedIdentityKernel = artifacts.require("./tests/UpdatedIdentityKernel.sol");
contract('IdentityFactory', function(accounts) {
let identityFactory;
let identity;
let updatedIdentity;
let updatedIdentityKernel;
before(async () => {
identityFactory = await IdentityFactory.new("0xaaa", {from: accounts[0]});
})
describe("IdentityFactory()", () => {
it("Creates a new identity", async () => {
let tx = await identityFactory.createIdentity({from: accounts[0]});
const logEntry = tx.logs[0];
assert.strictEqual(logEntry.event, "IdentityCreated");
identity = await Identity.at(logEntry.args.instance, {from: accounts[0]})
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
idUtils.purposes.MANAGEMENT,
identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
it("Registers a updated identity contract", async() => {
const infoHash = "0xbbb";
updatedIdentityKernel = await UpdatedIdentityKernel.new({from: accounts[0]});
await identityFactory.setKernel(updatedIdentityKernel.address, infoHash);
const newKernel = await TestUtils.listenForEvent(identityFactory.NewKernel());
assert(newKernel.infohash, infoHash, "Infohash is not correct");
});
it("Creates a new identity using latest version", async() => {
let tx = await identityFactory.createIdentity({from: accounts[0]});
const logEntry = tx.logs[0];
assert.strictEqual(logEntry.event, "IdentityCreated");
updatedIdentity = await UpdatedIdentityKernel.at(logEntry.args.instance, {from: accounts[0]})
tx = await updatedIdentity.test({from: accounts[0]});
assert.strictEqual(tx.logs[0].event, "TestFunctionExecuted");
// Test if it still executes identity functions as expected
let baseIdentity = await Identity.at(updatedIdentity.address, {from: accounts[0]})
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
1,
identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
it("Updates an identity to the latest version", async() => {
let tx1 = await identity.execute(
identity.address,
0,
idUtils.encode.updateRequestUpdatableInstance(updatedIdentityKernel.address),
{from: accounts[0]}
);
assert.strictEqual(tx1.logs[tx1.logs.length - 1].event, "Executed");
// Updating EVM timestamp to test delay
const plus31days = 60 * 60 * 24 * 31;
web3.currentProvider.send({jsonrpc: "2.0", method: "evm_increaseTime", params: [plus31days], id: 0});
web3.currentProvider.send({jsonrpc: "2.0", method: "evm_mine", params: [], id: 0})
// Confirm update
let tx2 = await identity.execute(
identity.address,
0,
idUtils.encode.updateConfirmUpdatableInstance(updatedIdentityKernel.address),
{from: accounts[0]}
);
assert.strictEqual(tx2.logs[tx2.logs.length - 1].event, "Executed");
// Calling function available in updated identity kernel
let updatedIdentity1 = await UpdatedIdentityKernel.at(identity.address, {from: accounts[0]})
let tx3 = await updatedIdentity1.test({from: accounts[0]});
assert.strictEqual(tx3.logs[tx3.logs.length - 1].event, "TestFunctionExecuted");
assert.equal(
tx3.logs[tx3.logs.length - 1].args.minApprovalsByManagementKeys.toString(10),
1,
identity.address + " wasn't updated to last version");
})
});
});

View File

@ -1,5 +1,9 @@
const TestUtils = require("./TestUtils.js")
const TestUtils = require("../utils/testUtils.js");
const web3EthAbi = require("web3-eth-abi");
const idUtils = require('../utils/identityUtils.js');
const Identity = artifacts.require("./identity/Identity.sol");
const TestContract = artifacts.require("./test/TestContract.sol");
contract('Identity', function(accounts) {
@ -9,126 +13,205 @@ contract('Identity', function(accounts) {
identity = await Identity.new({from: accounts[0]})
})
describe("Identity()", () => {
it("initialize with msg.sender as management key", async () => {
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
1,
idUtils.purposes.MANAGEMENT,
identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
});
describe("addKey(address _key, uint256 _type)", () => {
it("MANAGEMENT_KEY add a new address as ACTION_KEY", async () => {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
2,
idUtils.purposes.ACTION,
identity.address+".getKeyPurpose("+accounts[1]+") is not ACTION_KEY")
});
it("should not add key by non manager", async () => {
try {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[2]})
}catch(e){
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
{from: accounts[2]}
);
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
0,
idUtils.purposes.NONE,
identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should not add key type 1 by actor", async () => {
await identity.addKey(TestUtils.addressToBytes32(accounts[2]), 2, 1, {from: accounts[0]})
it("should not add key type 1 by actor", async () => {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
try {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[2]})
} catch(e){
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
{from: accounts[2]}
);
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
0,
idUtils.purposes.NONE,
identity.address+".getKeyType("+accounts[1]+") is not correct")
});
it("fire KeyAdded(address indexed key, uint256 indexed type)", async () => {
identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
{from: accounts[0]}
);
const keyAdded = await TestUtils.listenForEvent(identity.KeyAdded())
assert(keyAdded.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct")
assert(keyAdded.keyType, 2, "Type is not correct")
assert(keyAdded.keyType, idUtils.types.ADDRESS, "Type is not correct")
});
});
describe("removeKey(address _key, uint256 _type)", () => {
it("MANAGEMENT_KEY should remove a key", async () => {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
{from: accounts[0]}
);
it("MANAGEMENT_KEY should removes a key", async () => {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[0]})
await identity.removeKey(TestUtils.addressToBytes32(accounts[0]), 1, {from: accounts[1]})
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
0,
identity.address+".getKeyPurpose("+accounts[0]+") is not 0")
});
it("other key should not removes a key", async () => {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[0]})
try {
await identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 1, {from: accounts[2]})
}catch (e) {
}
await identity.execute(
identity.address,
0,
idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT),
{from: accounts[0]}
);
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
1,
idUtils.purposes.NONE,
identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
});
it("other key should not remove a key", async () => {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
{from: accounts[0]}
);
try {
await identity.execute(
identity.address,
0,
idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT),
{from: accounts[2]}
);
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
idUtils.purposes.MANAGEMENT,
identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
});
it("actor key should not remove key", async () => {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
await identity.addKey(TestUtils.addressToBytes32(accounts[2]), 2, 1, {from: accounts[0]})
try {
await identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 1, {from: accounts[2]})
}catch (e) {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
try {
await identity.execute(
identity.address,
0,
idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION),
{from: accounts[2]}
);
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
2,
idUtils.purposes.ACTION,
identity.address+".getKeyType("+accounts[1]+") is not 0")
});
it("MANAGEMENT_KEY should not remove itself MANAGEMENT_KEY when there is no other MANAGEMENT_KEY", async () => {
let assertJump = (error) => {
assert.isAbove(error.message.search('revert'), -1, 'Revert should happen');
}
try {
await identity.removeKey(TestUtils.addressToBytes32(accounts[0]), 1, {from: accounts[0]});
assert.fail('should have reverted before');
} catch(error) {
assertJump(error);
}
await identity.execute(
identity.address,
0,
idUtils.encode.removeKey(accounts[0], idUtils.purposes.MANAGEMENT),
{from: accounts[0]}
);
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
idUtils.purposes.MANAGEMENT,
identity.address+".getKeyType("+accounts[0]+") is not 1")
});
it("fire KeyRemoved(address indexed key, uint256 indexed type)", async () => {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1,{from: accounts[0]})
identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 2, {from: accounts[0]})
const keyRemoved = await TestUtils.listenForEvent(identity.KeyRemoved())
assert(keyRemoved.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct")
assert(keyRemoved.keyType, 2, "Type is not correct")
});
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
await identity.execute(
identity.address,
0,
idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION),
{from: accounts[0]}
);
const keyRemoved = await TestUtils.listenForEvent(identity.KeyRemoved());
assert(keyRemoved.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct");
assert(keyRemoved.keyType, idUtils.types.ADDRESS, "Type is not correct");
});
});
@ -137,34 +220,46 @@ contract('Identity', function(accounts) {
it("should start only with initializer as only key", async () => {
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
1,
idUtils.purposes.MANAGEMENT,
identity.address+".getKeyPurpose("+accounts[0]+") is not correct")
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
0,
idUtils.purposes.NONE,
identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should get type 2 after addKey type 2", async () => {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
2,
idUtils.purposes.ACTION,
identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should get type 3 after addKey type 3", async () => {
await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 3, 1, {from: accounts[0]})
it("should get type 3 after addKey type 3", async () => {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.CLAIM_SIGNER, idUtils.types.ADDRESS),
{from: accounts[0]}
);
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
3,
idUtils.purposes.CLAIM_SIGNER,
identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
});
/*
describe("getKeysByType(uint256 _type)", () => {
it("at initialization", async () => {
@ -178,36 +273,179 @@ contract('Identity', function(accounts) {
it("after removeKey", async () => {
});
it("after replaceKey", async () => {
});
});
*/
describe("execute(address _to, uint256 _value, bytes _data)", () => {
let testContractInstance;
let functionPayload;
it("Identity should receive ether", async() => {
const amountToSend = web3.toWei(0.05, "ether");
let idBalance0 = web3.eth.getBalance(identity.address);
await web3.eth.sendTransaction({from:accounts[0], to:identity.address, value: amountToSend})
let idBalance1 = web3.eth.getBalance(identity.address);
assert.equal(idBalance0.toNumber() + amountToSend, idBalance1.toNumber(), identity.address + " did not receive ether");
});
it("ACTOR_KEY execute arbitrary transaction", async () => {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
testContractInstance = await TestContract.new({from: accounts[0]});
functionPayload = web3EthAbi.encodeFunctionCall({
name: 'test',
type: 'function',
inputs: []
}, []);
await identity.execute(
testContractInstance.address,
0,
functionPayload,
{from: accounts[1]}
);
assert.notEqual(
await TestUtils.listenForEvent(testContractInstance.TestFunctionExecuted()),
undefined,
"Test function was not executed");
});
it("MANAGEMENT_KEY execute arbitrary transaction", async () => {
it("MANAGEMENT_KEY cannot execute arbitrary transaction", async () => {
try {
await identity.execute(
testContractInstance.address,
0,
functionPayload,
{from: accounts[0]}
);
} catch(error) {
TestUtils.assertJump(error);
}
});
it("Other keys NOT execute arbitrary transaction", async () => {
try {
await identity.execute(
testContractInstance.address,
0,
functionPayload,
{from: accounts[3]}
);
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
});
it("ACTION_KEY should send ether from contract", async () => {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
// Adding funds to contract
await web3.eth.sendTransaction({from:accounts[0], to:identity.address, value: web3.toWei(0.05, "ether")})
const amountToSend = web3.toWei(0.01, "ether");
let idBalance0 = web3.eth.getBalance(identity.address);
let a2Balance0 = web3.eth.getBalance(accounts[2]);
await identity.execute(
accounts[2],
amountToSend,
'',
{from: accounts[1]}
);
let idBalance1 = web3.eth.getBalance(identity.address);
let a2Balance1 = web3.eth.getBalance(accounts[2]);
assert(idBalance1.toNumber, idBalance0.toNumber - amountToSend, "Contract did not send ether");
assert(a2Balance1.toNumber, a2Balance0.toNumber + amountToSend, accounts[2] + " did not receive ether");
});
it("fire ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
await identity.execute(
testContractInstance.address,
0,
functionPayload,
{from: accounts[1]}
);
const executionRequested = await TestUtils.listenForEvent(identity.ExecutionRequested());
assert(executionRequested.to, testContractInstance.address, "To is not correct");
assert(executionRequested.value, 0, "Value is not correct");
assert(executionRequested.data, functionPayload, "Data is not correct");
});
it("fire Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
await identity.execute(
identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
{from: accounts[0]}
);
await identity.execute(
testContractInstance.address,
0,
functionPayload,
{from: accounts[1]}
);
const executed = await TestUtils.listenForEvent(identity.Executed());
assert(executed.to, testContractInstance.address, "To is not correct");
assert(executed.value, 0, "Value is not correct");
assert(executed.data, functionPayload, "Data is not correct");
});
});
/*
describe("setMinimumApprovalsByKeyPurpose(uint256 _type, uint8 _minimumApprovals)", () => {
it("MANAGEMENT_KEY should set minimum approvals for MANAGEMENT_KEYs", async () => {
});
it("MANAGEMENT_KEY should set minimum approvals for ACTION_KEYs", async () => {
});
it("ACTION_KEY should not be able to set minimum approvals", async () => {
});
it("Other keys should not be able to set minimum approvals", async () => {
});
});
describe("approve(bytes32 _id, bool _approve)", () => {
it("MANAGEMENT_KEY should approve a claim", async () => {
@ -218,6 +456,10 @@ contract('Identity', function(accounts) {
});
it("2 out of 3 MANAGEMENT_KEY should approve a transaction and execute it", async () => {
});
it("fire Approved(uint256 indexed executionId, bool approved)", async () => {
});
@ -259,5 +501,5 @@ contract('Identity', function(accounts) {
});
});
*/
});

64
test/identityExtended.js Normal file
View File

@ -0,0 +1,64 @@
const TestUtils = require("../utils/testUtils.js")
var ethUtils = require('ethereumjs-util')
const Identity = artifacts.require("./identity/Identity.sol");
contract('Identity - Extended Functionality', function(accounts) {
let identity;
beforeEach(async () => {
identity = await Identity.new({from: accounts[0]});
})
describe("Identity()", () => {
let privateKey = new Buffer('61bffea9215f65164ad18b45aff1436c0c165d0d5dd2087ef61b4232ba6d2c1a', 'hex')
let publicKey = ethUtils.privateToPublic(privateKey);
let pkSha = web3.sha3(publicKey.toString('hex'), {encoding: 'hex'});
it("Add ECDSA Management Key", async () => {
await identity.addKey(pkSha, 2, 1, {from: accounts[0]})
await identity.addPublicKey(pkSha, '0x' + publicKey.toString('hex'), {from: accounts[0]});
assert.equal(
await identity.getPublicKey(pkSha, {from: accounts[0]}),
'0x' + publicKey.toString('hex'),
identity.address+".getPublicKey("+pkSha+") is not correct");
});
it("Test Execution", async () => {
let to = accounts[1];
let value = 100;
let data = '';
let message = ethUtils.toBuffer("SignedMessage");
let msgHash = ethUtils.hashPersonalMessage(message);
let sig = ethUtils.ecsign(msgHash, privateKey);
let r = '0x' + sig.r.toString('hex');
let s = '0x' + sig.s.toString('hex');
let v = sig.v;
await identity.addKey(pkSha, 2, 1, {from: accounts[0]})
await identity.addPublicKey(pkSha, '0x' + publicKey.toString('hex'), {from: accounts[0]});
let tx = await identity.executeECDSA(to, value, data, pkSha, '0x' + msgHash.toString('hex'), v, r, s, {from: accounts[0]});
// TODO Assert ExecutionRequested Event
console.log(tx)
});
});
});

191
utils/identityUtils.js Normal file
View File

@ -0,0 +1,191 @@
const web3EthAbi = require("web3-eth-abi");
const _types = {
ADDRESS: 0,
ECDSA: 1,
RSA: 2
}
const _purposes = {
MANAGEMENT: 1,
ACTION: 2,
CLAIM_SIGNER: 3,
ENCRYPTION: 4,
NONE: 0
}
const hexToBytes32 = (input) => {
input = input.replace(/^0x/i,'');
const stringed = "0000000000000000000000000000000000000000000000000000000000000000" + input;
return "0x" + stringed.substring(stringed.length - 64, stringed.length);
}
const _addKey = function(key, purpose, type){
if(!/^(0x)?[0-9a-f]{0,64}$/i.test(key))
throw new Error('Key "'+ key +'" is not a valid hex string');
if (Object.values(_purposes).indexOf(purpose) == -1)
throw new Error('Purpose "'+ purpose +'" is not a valid purpose');
if (Object.values(_types).indexOf(type) == -1)
throw new Error('Type "'+ type +'" is not a valid type');
return web3EthAbi.encodeFunctionCall({
name: 'addKey',
type: 'function',
inputs: [{
type: 'bytes32',
name: '_key'
},{
type: 'uint256',
name: '_purpose'
},{
type: 'uint256',
name: '_type'
}]
}, [hexToBytes32(key), purpose, type]);
}
const _removeKey = function(key, purpose){
if(!/^(0x)?[0-9a-f]{0,64}$/i.test(key))
throw new Error('Key "'+ key +'" is not a valid hex string');
if (Object.values(_purposes).indexOf(purpose) == -1)
throw new Error('Purpose "'+ purpose +'" is not a valid purpose');
return web3EthAbi.encodeFunctionCall({
name: 'removeKey',
type: 'function',
inputs: [{
type: 'bytes32',
name: '_key'
},{
type: 'uint256',
name: '_purpose'
}]
}, [hexToBytes32(key), purpose]);
}
const _setMinimumApprovalsByKeyType = function(type, minimumApprovals) {
if (Object.values(_types).indexOf(type) == -1)
throw new Error('Type "'+ type +'" is not a valid type');
// TODO valdate minimumApprovals
return web3EthAbi.encodeFunctionCall({
name: 'setMinimumApprovalsByKeyType',
type: 'function',
inputs: [{
type: 'uint256',
name: '_type'
},{
type: 'uint8',
name: '_minimumApprovals'
}]
}, arguments);
}
const _setupRecovery = function(address){
if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
return web3EthAbi.encodeFunctionCall({
name: 'setupRecovery',
type: 'function',
inputs: [{
type: 'address',
name: '_recoveryContract'
}]
}, [address]);
}
const _managerReset = function(address){
if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
return web3EthAbi.encodeFunctionCall({
name: 'managerReset',
type: 'function',
inputs: [{
type: 'address',
name: '_newKey'
}]
}, [address]);
}
const _updateUpdatableInstance = function(address){
if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
return web3EthAbi.encodeFunctionCall({
name: 'updateUpdatableInstance',
type: 'function',
inputs: [{
type: 'address',
name: '_kernel'
}]
}, [address]);
}
const _updateRequestUpdatableInstance = function(address){
if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
return web3EthAbi.encodeFunctionCall({
name: 'updateRequestUpdatableInstance',
type: 'function',
inputs: [{
type: 'address',
name: '_kernel'
}]
}, [address]);
}
const _updateConfirmUpdatableInstance = function(address){
if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
return web3EthAbi.encodeFunctionCall({
name: 'updateConfirmUpdatableInstance',
type: 'function',
inputs: [{
type: 'address',
name: '_kernel'
}]
}, [address]);
}
const _updateCancelUpdatableInstance = function(address){
if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
return web3EthAbi.encodeFunctionCall({
name: 'updateCancelUpdatableInstance',
type: 'function',
inputs: []
}, []);
}
module.exports = {
types: _types,
purposes: _purposes,
encode: {
addKey: _addKey,
removeKey: _removeKey,
setMinimumApprovalsByKeyType: _setMinimumApprovalsByKeyType,
setupRecovery: _setupRecovery,
managerReset: _managerReset,
updateUpdatableInstance: _updateUpdatableInstance,
updateRequestUpdatableInstance: _updateRequestUpdatableInstance,
updateConfirmUpdatableInstance: _updateConfirmUpdatableInstance,
updateCancelUpdatableInstance: _updateCancelUpdatableInstance
}
}