impl spec (WIP)
This commit is contained in:
parent
7801ece88a
commit
73da535f65
|
@ -23,7 +23,7 @@ contract MultisigRecovery {
|
|||
//just used offchain
|
||||
mapping(address => uint256) public nonce;
|
||||
//flag approvals
|
||||
mapping(bytes32 => mapping(bytes32=>bool)) public approved;
|
||||
mapping(bytes32 => mapping(bytes32 => bool)) public approved;
|
||||
//storage for pending setup
|
||||
mapping(address => RecoverySet) public pending;
|
||||
//storage for active recovery
|
||||
|
@ -50,15 +50,6 @@ contract MultisigRecovery {
|
|||
{
|
||||
ens = _ens;
|
||||
}
|
||||
/**
|
||||
* @notice Cancels a pending setup to change the recovery parameters
|
||||
*/
|
||||
function cancelSetup()
|
||||
external
|
||||
{
|
||||
delete pending[msg.sender];
|
||||
emit SetupRequested(msg.sender, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Configure recovery parameters of `msg.sender`. `emit Activated(msg.sender)` if there was no previous setup, or `emit SetupRequested(msg.sender, now()+setupDelay)` when reconfiguring.
|
||||
|
@ -100,6 +91,16 @@ contract MultisigRecovery {
|
|||
emit Activated(_who);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Cancels a pending setup to change the recovery parameters
|
||||
*/
|
||||
function cancelSetup()
|
||||
external
|
||||
{
|
||||
delete pending[msg.sender];
|
||||
emit SetupRequested(msg.sender, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Approves a recovery. This method is important for when the address is an contract and dont implements EIP1271.
|
||||
* @param _approveHash Hash of the recovery call
|
||||
|
@ -214,7 +215,7 @@ contract MultisigRecovery {
|
|||
),
|
||||
"Invalid ENS entry"
|
||||
);
|
||||
bytes32 leaf = keccak256(abi.encodePacked(isENS, isENS ? _ensNode : bytes32(uint256(_signer))));
|
||||
bytes32 leaf = keccak256(isENS ? abi.encodePacked(byte(0x01), _ensNode) : abi.encodePacked(byte(0x00), bytes32(uint256(_signer))));
|
||||
approved[leaf][_approveHash] = true;
|
||||
emit Approved(_approveHash, leaf);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ config({
|
|||
});
|
||||
|
||||
contract('MultiMerkleProof', function () {
|
||||
|
||||
describe('calculateMultiMerkleRoot', function () {
|
||||
it('display cost of deploy MerkleMultiProofWrapper', async function () {
|
||||
await MerkleMultiProofWrapper.methods.foo().send();
|
||||
|
@ -96,8 +97,25 @@ contract('MultiMerkleProof', function () {
|
|||
}
|
||||
assert(invalid);
|
||||
});
|
||||
|
||||
it(`cost of calculate Tree A root for ${leafsA.length} leafs using ${proofA.length} proofs and ${flagsA.length} flags`, async function () {
|
||||
await MerkleMultiProofWrapper.methods.calculateMultiMerkleRoot(
|
||||
leafsA,
|
||||
proofA,
|
||||
flagsA,
|
||||
).send()
|
||||
});
|
||||
|
||||
it('calculate merkle root from leafs, proofs and flags (fuzzy)', async function () {
|
||||
it(`cost of calculate Tree B root for ${leafsB.length} leafs using ${proofB.length} proofs and ${flagsB.length} flags`, async function () {
|
||||
await MerkleMultiProofWrapper.methods.calculateMultiMerkleRoot(
|
||||
leafsB,
|
||||
proofB,
|
||||
flagsB,
|
||||
).send()
|
||||
});
|
||||
|
||||
it(`calculate ${fuzzyProofChecks} merkle root from leafs, proofs and flags (fuzzy)`, async function () {
|
||||
this.timeout(500*fuzzyProofChecks);
|
||||
for(let j = 0; j < fuzzyProofChecks; j++){
|
||||
const leafsFuzzy = merkleTreeA.getElements(
|
||||
Array.from({length: leafsSize}, () => elementsA[Math.floor(Math.random()*elementsA.length)] ).filter((value, index, self) => self.indexOf(value) === index)
|
||||
|
@ -113,7 +131,8 @@ contract('MultiMerkleProof', function () {
|
|||
}
|
||||
});
|
||||
|
||||
it('return wrong root for invalid Merkle proof (fuzzy)', async function () {
|
||||
it(`calculate ${fuzzyProofChecks} wrong merkle root from wrong proofs and flags (fuzzy)`, async function () {
|
||||
this.timeout(500*fuzzyProofChecks);
|
||||
for(let j = 0; j < fuzzyProofChecks; j++){
|
||||
const leafsFuzzy = merkleTreeB.getElements(
|
||||
Array.from({length: leafsSize}, () => elementsB[Math.floor(Math.random()*elementsB.length)] ).filter((value, index, self) => self.indexOf(value) === index)
|
||||
|
@ -133,21 +152,5 @@ contract('MultiMerkleProof', function () {
|
|||
assert(invalid);
|
||||
}
|
||||
});
|
||||
|
||||
it(`cost of calculate Tree A root for ${leafsA.length} leafs using ${proofA.length} proofs and ${flagsA.length} flags`, async function () {
|
||||
await MerkleMultiProofWrapper.methods.calculateMultiMerkleRoot(
|
||||
leafsA,
|
||||
proofA,
|
||||
flagsA,
|
||||
).send()
|
||||
});
|
||||
|
||||
it(`cost of calculate Tree B root for ${leafsB.length} leafs using ${proofB.length} proofs and ${flagsB.length} flags`, async function () {
|
||||
await MerkleMultiProofWrapper.methods.calculateMultiMerkleRoot(
|
||||
leafsB,
|
||||
proofB,
|
||||
flagsB,
|
||||
).send()
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
const { SecretMultisig } = require('../utils/secretMultisig.js');
|
||||
const EmbarkJS = require('Embark/EmbarkJS');
|
||||
const MultisigRecovery = require('Embark/contracts/MultisigRecovery')
|
||||
|
||||
let accounts;
|
||||
let ms;
|
||||
|
||||
config({
|
||||
blockchain: {
|
||||
accounts: [
|
||||
{
|
||||
mnemonic: "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat",
|
||||
addressIndex: "0",
|
||||
numAddresses: "102",
|
||||
balance: "5 ether"
|
||||
}
|
||||
]
|
||||
},
|
||||
namesystem: {
|
||||
enabled: true,
|
||||
register: {
|
||||
rootDomain: "eth",
|
||||
subdomains: {
|
||||
'test': '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'
|
||||
}
|
||||
}
|
||||
},
|
||||
contracts: {
|
||||
deploy: {
|
||||
"MultisigRecovery": {
|
||||
args: [ "$ENSRegistry" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}, (_err, web3_accounts) => {
|
||||
accounts = web3_accounts;
|
||||
ms = new SecretMultisig(accounts[0], "0x0011223344556677889900112233445566778899001122334455667788990011", accounts.slice(1));
|
||||
});
|
||||
|
||||
|
||||
contract('SecretMultisigRecovery', function () {
|
||||
|
||||
describe('setup', function () {
|
||||
it('first time activates immediately', async function () {
|
||||
|
||||
});
|
||||
|
||||
it('pending setup', async function () {
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('activate', function () {
|
||||
it('does not activate during delay time', async function () {
|
||||
|
||||
});
|
||||
|
||||
it('activates a pending setup', async function () {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelSetup', function () {
|
||||
it('cancels when not reached', async function () {
|
||||
|
||||
});
|
||||
|
||||
it('does not cancel when reached', async function () {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('approve', function () {
|
||||
it('using address', async function () {
|
||||
|
||||
});
|
||||
it('using ENS', async function () {
|
||||
let address = await EmbarkJS.Names.resolve('test.eth')
|
||||
console.log('ENS address', address);
|
||||
});
|
||||
});
|
||||
|
||||
describe('approvePreSigned', function () {
|
||||
it('using address', async function () {
|
||||
|
||||
});
|
||||
it('using ENS', async function () {
|
||||
let address = await EmbarkJS.Names.resolve('test.eth')
|
||||
console.log('ENS address', address);
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute', function () {
|
||||
it('executes approved', async function () {
|
||||
|
||||
});
|
||||
|
||||
it('cant execute with low threshold', async function () {
|
||||
|
||||
});
|
||||
|
||||
it('cant execute with different calldest', async function () {
|
||||
|
||||
});
|
||||
|
||||
it('cant execute with different calldata', async function () {
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
const { MerkleTree } = require('./merkleTree.js');
|
||||
const { keccak256, bufferToHex, isValidAddress, setLengthLeft } = require('ethereumjs-util');
|
||||
const namehash = require('eth-ens-namehash');
|
||||
|
||||
const MINIMUM_LIST_SIZE = 4096;
|
||||
const THRESHOLD = 100 * 10**18;
|
||||
const RECOVERY_ADDRESS = "0x2429242924292429242924292429242924292429";
|
||||
const ERC2429 = require('Embark/contracts/MultisigRecovery');
|
||||
|
||||
export default class SecretMultisig {
|
||||
|
||||
constructor(userAddress, privateHash, addressList) {
|
||||
if (addressList.length == 0){
|
||||
throw new Error("Invalid Address List")
|
||||
}
|
||||
|
||||
this.elements = addressList.map((v) => this.hashLeaf(v.address, v.weight));
|
||||
if(this.elements.length < MINIMUM_LIST_SIZE) {
|
||||
this.elements.push(... Array.from({length: MINIMUM_LIST_SIZE-this.elements.length}, (v,k) => hashFakeLeaf(privateHash, k)))
|
||||
}
|
||||
this.merkleTree = new MerkleTree(this.elements);
|
||||
this.executeHash = this.hashExecute(privateHash, userAddress);
|
||||
this.partialReveal = keccak256(this.executeHash);
|
||||
this.publicHash = keccak256(Buffer.concat(
|
||||
this.partialReveal,
|
||||
keccak256(this.merkleTree.getRoot())
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
hashExecute = async (privateHash, userAddress) => keccak256(Buffer.concat(
|
||||
privateHash,
|
||||
Buffer,from(ERC2429.address, 'hex'),
|
||||
setLengthLeft(Buffer.from(Number.toString(await ERC2429.methods.nonce(userAddress).call(), 16), 'hex'), 32)
|
||||
));
|
||||
|
||||
|
||||
hashLeaf = (ethereumAddress, weight) => keccak256(Buffer.concat(
|
||||
this.hashAddress(ethereumAddress),
|
||||
setLengthLeft(Buffer.from(Number.toString(weight, 16), 'hex'), 32)
|
||||
));
|
||||
|
||||
hashFakeLeaf = (privateHash, position) => keccak256(Buffer.concat(
|
||||
privateHash,
|
||||
setLengthLeft(Buffer.from(Number.toString(position, 16), 'hex'), 32)
|
||||
));
|
||||
|
||||
hashAddress = (ethereumAddress) => keccak256(
|
||||
isValidAddress(ethereumAddress) ? Buffer.concat(
|
||||
Buffer.from('0x00', 'hex'),
|
||||
setLengthLeft(Buffer.from(ethereumAddress, 'hex'), 32)
|
||||
) : Buffer.concat(
|
||||
Buffer.from('0x01', 'hex'),
|
||||
namehash(ethereumAddress)
|
||||
)
|
||||
);
|
||||
|
||||
hashApproval = (approver_address, calldest, calldata) => keccak256(Buffer.concat(
|
||||
this.hashAddress(approver_address),
|
||||
this.hashCall(calldest, calldata)
|
||||
));
|
||||
|
||||
|
||||
hashCall = (calldest, calldata) => keccak256(Buffer.concat(
|
||||
this.partialReveal,
|
||||
Buffer.from(calldest, 'hex'),
|
||||
Buffer.from(calldata, 'hex')
|
||||
));
|
||||
|
||||
}
|
Loading…
Reference in New Issue