diff --git a/rust/optics-base/src/abis/Home.abi.json b/rust/optics-base/src/abis/Home.abi.json index 8cc129048..d4dcc88c8 100644 --- a/rust/optics-base/src/abis/Home.abi.json +++ b/rust/optics-base/src/abis/Home.abi.json @@ -51,9 +51,9 @@ "inputs": [ { "indexed": false, - "internalType": "bytes32[2]", + "internalType": "bytes32", "name": "_oldRoot", - "type": "bytes32[2]" + "type": "bytes32" }, { "indexed": false, @@ -143,9 +143,9 @@ { "inputs": [ { - "internalType": "bytes32[2]", + "internalType": "bytes32", "name": "_oldRoot", - "type": "bytes32[2]" + "type": "bytes32" }, { "internalType": "bytes32[2]", @@ -233,6 +233,38 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_item", + "type": "bytes32" + } + ], + "name": "queueContains", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "queueEnd", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "root", diff --git a/rust/optics-base/src/abis/ProcessingReplica.abi.json b/rust/optics-base/src/abis/ProcessingReplica.abi.json index e60ba62ab..1784c37cf 100644 --- a/rust/optics-base/src/abis/ProcessingReplica.abi.json +++ b/rust/optics-base/src/abis/ProcessingReplica.abi.json @@ -40,9 +40,9 @@ "inputs": [ { "indexed": false, - "internalType": "bytes32[2]", + "internalType": "bytes32", "name": "_oldRoot", - "type": "bytes32[2]" + "type": "bytes32" }, { "indexed": false, @@ -178,9 +178,9 @@ { "inputs": [ { - "internalType": "bytes32[2]", + "internalType": "bytes32", "name": "_oldRoot", - "type": "bytes32[2]" + "type": "bytes32" }, { "internalType": "bytes32[2]", @@ -317,12 +317,12 @@ "outputs": [ { "internalType": "bool", - "name": "", + "name": "_success", "type": "bool" }, { "internalType": "bytes", - "name": "", + "name": "_ret", "type": "bytes" } ], @@ -386,6 +386,38 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_item", + "type": "bytes32" + } + ], + "name": "queueContains", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "queueEnd", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "state", diff --git a/solidity/contracts/Common.sol b/solidity/contracts/Common.sol index df493339b..536e1aebd 100644 --- a/solidity/contracts/Common.sol +++ b/solidity/contracts/Common.sol @@ -98,7 +98,7 @@ abstract contract Common { bytes signature ); event DoubleUpdate( - bytes32[2] _oldRoot, + bytes32 _oldRoot, bytes32[2] _newRoot, bytes _signature, bytes _signature2 @@ -123,7 +123,7 @@ abstract contract Common { } modifier notFailed() { - require(state != States.FAILED); + require(state != States.FAILED, "failed state"); _; } @@ -138,16 +138,18 @@ abstract contract Common { return ECDSA.recover(_digest, _signature) == updater; } + // Checks that updater signed both updates and that + // the two updates are not equal (i.e. conflicting) function doubleUpdate( - bytes32[2] calldata _oldRoot, + bytes32 _oldRoot, bytes32[2] calldata _newRoot, bytes calldata _signature, bytes calldata _signature2 ) external notFailed { if ( - Common.checkSig(_newRoot[0], _oldRoot[0], _signature) && - Common.checkSig(_newRoot[1], _oldRoot[1], _signature2) && - (_newRoot[0] != _newRoot[1] || _oldRoot[0] != _oldRoot[1]) + Common.checkSig(_oldRoot, _newRoot[0], _signature) && + Common.checkSig(_oldRoot, _newRoot[1], _signature2) && + _newRoot[0] != _newRoot[1] ) { fail(); emit DoubleUpdate(_oldRoot, _newRoot, _signature, _signature2); diff --git a/solidity/contracts/Home.sol b/solidity/contracts/Home.sol index 7d8d00ef8..9e6c57e56 100644 --- a/solidity/contracts/Home.sol +++ b/solidity/contracts/Home.sol @@ -66,7 +66,7 @@ contract Home is MerkleTreeManager, QueueManager, Common { bytes32 _newRoot, bytes memory _signature ) external notFailed { - if (improperUpdate(_newRoot, _oldRoot, _signature)) return; + if (improperUpdate(_oldRoot, _newRoot, _signature)) return; while (true) { bytes32 next = queue.dequeue(); if (next == _newRoot) break; @@ -79,8 +79,8 @@ contract Home is MerkleTreeManager, QueueManager, Common { bytes32 _newRoot, bytes memory _signature ) public notFailed returns (bool) { - require(Common.checkSig(_newRoot, _oldRoot, _signature), "bad sig"); - require(_oldRoot == current, "Not a current update"); + require(Common.checkSig(_oldRoot, _newRoot, _signature), "bad sig"); + require(_oldRoot == current, "not a current update"); if (!queue.contains(_newRoot)) { fail(); emit ImproperUpdate(); diff --git a/solidity/contracts/Queue.sol b/solidity/contracts/Queue.sol index 95ff31bb4..0a3c44ccc 100644 --- a/solidity/contracts/Queue.sol +++ b/solidity/contracts/Queue.sol @@ -79,7 +79,7 @@ library QueueLib { view returns (bool) { - for (uint256 i = _q.first; i < _q.last; i++) { + for (uint256 i = _q.first; i <= _q.last; i++) { if (_q.queue[i] == _item) { return true; } @@ -123,4 +123,12 @@ contract QueueManager { constructor() { queue.init(); } + + function queueContains(bytes32 _item) external view returns (bool) { + return queue.contains(_item); + } + + function queueEnd() external view returns (bytes32) { + return queue.lastItem(); + } } diff --git a/solidity/contracts/test/TestHome.sol b/solidity/contracts/test/TestHome.sol new file mode 100644 index 000000000..5f225886d --- /dev/null +++ b/solidity/contracts/test/TestHome.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.6.11; + +import "../Home.sol"; + +contract TestHome is Home { + using QueueLib for QueueLib.Queue; + using MerkleLib for MerkleLib.Tree; + + constructor(uint32 _originSLIP44, address _sortition) Home(_originSLIP44, _sortition) {} + + function setFailed() public { + _setFailed(); + } +} \ No newline at end of file diff --git a/solidity/contracts/test/TestQueue.sol b/solidity/contracts/test/TestQueue.sol index b26602300..a68a2df2d 100644 --- a/solidity/contracts/test/TestQueue.sol +++ b/solidity/contracts/test/TestQueue.sol @@ -9,7 +9,12 @@ contract TestQueue is QueueManager { constructor() QueueManager() {} // NB: this is unfortunately expensive - function contains(bytes32 _item) external view returns (bool) { + + function contains(bytes32 _item) + external + view + returns (bool) + { return queue.contains(_item); } @@ -21,7 +26,10 @@ contract TestQueue is QueueManager { return queue.peek(); } - function enqueue(bytes32 _item) external returns (uint256 _last) { + function enqueue(bytes32 _item) + external + returns (uint256 _last) + { return queue.enqueue(_item); } @@ -56,4 +64,4 @@ contract TestQueue is QueueManager { function initAgain() external { queue.init(); } -} +} \ No newline at end of file diff --git a/solidity/lib/index.js b/solidity/lib/index.js index 203544146..ffb847b35 100644 --- a/solidity/lib/index.js +++ b/solidity/lib/index.js @@ -35,7 +35,7 @@ extendEnvironment((hre) => { this.address = address; } - async static fromSigner(signer, originSlip44) { + static async fromSigner(signer, originSlip44) { return new Updater(signer, await signer.getAddress(), originSlip44, true); } @@ -52,11 +52,12 @@ extendEnvironment((hre) => { async signUpdate(oldRoot, newRoot) { let message = this.message(oldRoot, newRoot); - let signature = await this.signer.signMessage(message); + let msgHash = ethers.utils.arrayify(ethers.utils.keccak256(message)); + let signature = await this.signer.signMessage(msgHash); return { origin: this.originSlip44, - newRoot, oldRoot, + newRoot, signature, }; } diff --git a/solidity/package-lock.json b/solidity/package-lock.json index 67eb61160..c00069399 100644 --- a/solidity/package-lock.json +++ b/solidity/package-lock.json @@ -187,9 +187,9 @@ } }, "@ethersproject/basex": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.0.7.tgz", - "integrity": "sha512-OsXnRsujGmYD9LYyJlX+cVe5KfwgLUbUJrJMWdzRWogrygXd5HvGd7ygX1AYjlu1z8W/+t2FoQnczDR/H2iBjA==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.0.8.tgz", + "integrity": "sha512-PCVKZIShBQUqAXjJSvaCidThPvL0jaaQZcewJc0sf8Xx05BizaOS8r3jdPdpNdY+/qZtRDqwHTSKjvR/xssyLQ==", "dev": true, "requires": { "@ethersproject/bytes": "^5.0.9", @@ -226,9 +226,9 @@ } }, "@ethersproject/contracts": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.0.9.tgz", - "integrity": "sha512-CCTxVeDh6sjdSEbjzONhtwPjECvaHE62oGkY8M7kP0CHmgLD2SEGel0HZib8e5oQKRKGly9AKcUFW4g3rQ0AQw==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.0.10.tgz", + "integrity": "sha512-h9kdvllwT6B1LyUXeNQIb7Y6u6ZprP5LUiQIjSqvOehhm1sFZcaVtydsSa0LIg3SBC5QF0M7zH5p7EtI2VD0rQ==", "dev": true, "requires": { "@ethersproject/abi": "^5.0.10", @@ -259,9 +259,9 @@ } }, "@ethersproject/hdnode": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.0.8.tgz", - "integrity": "sha512-Mscpjd7BBjxYSWghaNMwV0xrBBkOoCq6YEPRm9MgE24CiBlzzbfEB5DGq6hiZqhQaxPkdCUtKKqZi3nt9hx43g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.0.9.tgz", + "integrity": "sha512-S5UMmIC6XfFtqhUK4uTjD8GPNzSbE+sZ/0VMqFnA3zAJ+cEFZuEyhZDYnl2ItGJzjT4jsy+uEy1SIl3baYK1PQ==", "dev": true, "requires": { "@ethersproject/abstract-signer": "^5.0.10", @@ -279,9 +279,9 @@ } }, "@ethersproject/json-wallets": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.0.10.tgz", - "integrity": "sha512-Ux36u+d7Dm0M5AQ+mWuHdvfGPMN8K1aaLQgwzrsD4ELTWlwRuHuQbmn7/GqeOpbfaV6POLwdYcBk2TXjlGp/IQ==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.0.11.tgz", + "integrity": "sha512-0GhWScWUlXXb4qJNp0wmkU95QS3YdN9UMOfMSEl76CRANWWrmyzxcBVSXSBu5iQ0/W8wO+xGlJJ3tpA6v3mbIw==", "dev": true, "requires": { "@ethersproject/abstract-signer": "^5.0.10", @@ -325,9 +325,9 @@ } }, "@ethersproject/pbkdf2": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.0.7.tgz", - "integrity": "sha512-0SNLNixPMqnosH6pyc4yPiUu/C9/Jbu+f6I8GJW9U2qNpMBddmRJviwseoha5Zw1V+Aw0Z/yvYyzIIE8yPXqLA==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.0.8.tgz", + "integrity": "sha512-UlmAMGbIPaS2xXsI38FbePVTfJMuU9jnwcqVn3p88HxPF4kD897ha+l3TNsBqJqf32UbQL5GImnf1oJkSKq4vQ==", "dev": true, "requires": { "@ethersproject/bytes": "^5.0.9", @@ -344,9 +344,9 @@ } }, "@ethersproject/providers": { - "version": "5.0.19", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.0.19.tgz", - "integrity": "sha512-G+flo1jK1y/rvQy6b71+Nu7qOlkOKz+XqpgqFMZslkCzGuzQRmk9Qp7Ln4soK8RSyP1e5TCujaRf1H+EZahoaw==", + "version": "5.0.21", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.0.21.tgz", + "integrity": "sha512-KyH9TylyLqspbO/2C0ph+0ZpOnb/2GkKQtpcs7IyHZ/wHXdhbClLeaBdO0b4Fpo6zAZWjgIdN6WUOMGkyy7b6A==", "dev": true, "requires": { "@ethersproject/abstract-provider": "^5.0.8", @@ -371,9 +371,9 @@ } }, "@ethersproject/random": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.0.7.tgz", - "integrity": "sha512-PxSRWwN3s+FH9AWMZU6AcWJsNQ9KzqKV6NgdeKPtxahdDjCuXxTAuzTZNXNRK+qj+Il351UnweAGd+VuZcOAlQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.0.8.tgz", + "integrity": "sha512-4rHtotmd9NjklW0eDvByicEkL+qareIyFSbG1ShC8tPJJSAC0g55oQWzw+3nfdRCgBHRuEE7S8EcPcTVPvZ9cA==", "dev": true, "requires": { "@ethersproject/bytes": "^5.0.9", @@ -391,9 +391,9 @@ } }, "@ethersproject/sha2": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.0.7.tgz", - "integrity": "sha512-MbUqz68hhp5RsaZdqi1eg1rrtiqt5wmhRYqdA7MX8swBkzW2KiLgK+Oh25UcWhUhdi1ImU9qrV6if5j0cC7Bxg==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.0.8.tgz", + "integrity": "sha512-ILP1ZgyvDj4rrdE+AXrTv9V88m7x87uga2VZ/FeULKPumOEw/4bGnJz/oQ8zDnDvVYRCJ+48VaQBS2CFLbk1ww==", "dev": true, "requires": { "@ethersproject/bytes": "^5.0.9", @@ -426,9 +426,9 @@ } }, "@ethersproject/solidity": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.0.8.tgz", - "integrity": "sha512-OJkyBq9KaoGsi8E8mYn6LX+vKyCURvxSp0yuGBcOqEFM3vkn9PsCiXsHdOXdNBvlHG5evJXwAYC2UR0TzgJeKA==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.0.9.tgz", + "integrity": "sha512-LIxSAYEQgLRXE3mRPCq39ou61kqP8fDrGqEeNcaNJS3aLbmAOS8MZp56uK++WsdI9hj8sNsFh78hrAa6zR9Jag==", "dev": true, "requires": { "@ethersproject/bignumber": "^5.0.13", @@ -467,9 +467,9 @@ } }, "@ethersproject/units": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.0.9.tgz", - "integrity": "sha512-4jIkcMVrJ3lCgXMO4M/2ww0/T/IN08vJTZld7FIAwa6aoBDTAy71+sby3sShl1SG3HEeKYbI3fBWauCUgPRUpQ==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.0.10.tgz", + "integrity": "sha512-eaiHi9ham5lbC7qpqxpae7OY/nHJUnRUnFFuEwi2VB5Nwe3Np468OAV+e+HR+jAK4fHXQE6PFBTxWGtnZuO37g==", "dev": true, "requires": { "@ethersproject/bignumber": "^5.0.13", @@ -478,9 +478,9 @@ } }, "@ethersproject/wallet": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.0.10.tgz", - "integrity": "sha512-5siYr38NhqZKH6DUr6u4PdhgOKur8Q6sw+JID2TitEUmW0tOl8f6rpxAe77tw6SJT60D2UcvgsyLtl32+Nl+ig==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.0.11.tgz", + "integrity": "sha512-2Fg/DOvUltR7aZTOyWWlQhru+SKvq2UE3uEhXSyCFgMqDQNuc2nHXh1SHJtN65jsEbjVIppOe1Q7EQMvhmeeRw==", "dev": true, "requires": { "@ethersproject/abstract-provider": "^5.0.8", @@ -514,9 +514,9 @@ } }, "@ethersproject/wordlists": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.0.8.tgz", - "integrity": "sha512-px2mloc1wAcdTbzv0ZotTx+Uh/dfnDO22D9Rx8xr7+/PUwAhZQjoJ9t7Hn72nsaN83rWBXsLvFcIRZju4GIaEQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.0.9.tgz", + "integrity": "sha512-Sn6MTjZkfbriod6GG6+p43W09HOXT4gwcDVNj0YoPYlo4Zq2Fk6b1CU9KUX3c6aI17PrgYb4qwZm5BMuORyqyQ==", "dev": true, "requires": { "@ethersproject/bytes": "^5.0.9", @@ -673,9 +673,9 @@ } }, "@openzeppelin/contracts": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-3.3.0.tgz", - "integrity": "sha512-AemZEsQYtUp1WRkcmZm1div5ORfTpLquLaziCIrSagjxyKdmObxuaY1yjQ5SHFMctR8rLwp706NXTbiIRJg7pw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-3.4.0.tgz", + "integrity": "sha512-qh+EiHWzfY/9CORr+eRUkeEUP1WiFUcq3974bLHwyYzLBUtK6HPaMkIUHi74S1rDTZ0sNz42DwPc5A4IJvN3rg==", "dev": true }, "@resolver-engine/core": { @@ -1877,15 +1877,6 @@ "type-detect": "^4.0.5" } }, - "chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", - "dev": true, - "requires": { - "check-error": "^1.0.2" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -3388,47 +3379,79 @@ } }, "ethers": { - "version": "5.0.26", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.0.26.tgz", - "integrity": "sha512-MqA8Fvutn3qEW0yBJOHeV6KZmRpF2rqlL2B5058AGkUFsuu6j5Ns/FRlMsbGeQwBz801IB23jQp7vjRfFsKSkg==", - "dev": true, - "requires": { - "@ethersproject/abi": "5.0.10", - "@ethersproject/abstract-provider": "5.0.8", - "@ethersproject/abstract-signer": "5.0.11", - "@ethersproject/address": "5.0.9", - "@ethersproject/base64": "5.0.7", - "@ethersproject/basex": "5.0.7", - "@ethersproject/bignumber": "5.0.13", - "@ethersproject/bytes": "5.0.9", - "@ethersproject/constants": "5.0.8", - "@ethersproject/contracts": "5.0.9", - "@ethersproject/hash": "5.0.10", - "@ethersproject/hdnode": "5.0.8", - "@ethersproject/json-wallets": "5.0.10", - "@ethersproject/keccak256": "5.0.7", - "@ethersproject/logger": "5.0.8", - "@ethersproject/networks": "5.0.7", - "@ethersproject/pbkdf2": "5.0.7", - "@ethersproject/properties": "5.0.7", - "@ethersproject/providers": "5.0.19", - "@ethersproject/random": "5.0.7", - "@ethersproject/rlp": "5.0.7", - "@ethersproject/sha2": "5.0.7", - "@ethersproject/signing-key": "5.0.8", - "@ethersproject/solidity": "5.0.8", - "@ethersproject/strings": "5.0.8", - "@ethersproject/transactions": "5.0.9", - "@ethersproject/units": "5.0.9", - "@ethersproject/wallet": "5.0.10", - "@ethersproject/web": "5.0.12", - "@ethersproject/wordlists": "5.0.8" + "version": "5.0.28", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.0.28.tgz", + "integrity": "sha512-prYYCmZMGbrhP2PEXA2re5BpNPjaCP2y5gO1dh1i+fPxdkldQOk+0c0l8KlnxwUztKq4E40xpB0gyURdcAOaAg==", + "dev": true, + "requires": { + "@ethersproject/abi": "5.0.11", + "@ethersproject/abstract-provider": "5.0.9", + "@ethersproject/abstract-signer": "5.0.12", + "@ethersproject/address": "5.0.10", + "@ethersproject/base64": "5.0.8", + "@ethersproject/basex": "5.0.8", + "@ethersproject/bignumber": "5.0.14", + "@ethersproject/bytes": "5.0.10", + "@ethersproject/constants": "5.0.9", + "@ethersproject/contracts": "5.0.10", + "@ethersproject/hash": "5.0.11", + "@ethersproject/hdnode": "5.0.9", + "@ethersproject/json-wallets": "5.0.11", + "@ethersproject/keccak256": "5.0.8", + "@ethersproject/logger": "5.0.9", + "@ethersproject/networks": "5.0.8", + "@ethersproject/pbkdf2": "5.0.8", + "@ethersproject/properties": "5.0.8", + "@ethersproject/providers": "5.0.21", + "@ethersproject/random": "5.0.8", + "@ethersproject/rlp": "5.0.8", + "@ethersproject/sha2": "5.0.8", + "@ethersproject/signing-key": "5.0.9", + "@ethersproject/solidity": "5.0.9", + "@ethersproject/strings": "5.0.9", + "@ethersproject/transactions": "5.0.10", + "@ethersproject/units": "5.0.10", + "@ethersproject/wallet": "5.0.11", + "@ethersproject/web": "5.0.13", + "@ethersproject/wordlists": "5.0.9" }, "dependencies": { - "@ethersproject/abstract-signer": { + "@ethersproject/abi": { "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.0.11.tgz", - "integrity": "sha512-RKOgPSEYafknA62SrD3OCK42AllHE4YBfKYXyQeM+sBP7Nq3X5FpzeoY4uzC43P4wIhmNoTHCKQuwnX7fBqb6Q==", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.11.tgz", + "integrity": "sha512-ibZswQsjdFuLSfY2lbRTZM2Uk+ci7tp+mjVK0kjxVol2V32cb7va1r6B4AJU/Ac/VTstCjxtn0KKMfbkPc002w==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.0.9", + "@ethersproject/bignumber": "^5.0.13", + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/constants": "^5.0.8", + "@ethersproject/hash": "^5.0.10", + "@ethersproject/keccak256": "^5.0.7", + "@ethersproject/logger": "^5.0.8", + "@ethersproject/properties": "^5.0.7", + "@ethersproject/strings": "^5.0.8" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.0.9.tgz", + "integrity": "sha512-X9fMkqpeu9ayC3JyBkeeZhn35P4xQkpGX/l+FrxDtEW9tybf/UWXSMi8bGThpPtfJ6q6U2LDetXSpSwK4TfYQQ==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.0.13", + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/logger": "^5.0.8", + "@ethersproject/networks": "^5.0.7", + "@ethersproject/properties": "^5.0.7", + "@ethersproject/transactions": "^5.0.9", + "@ethersproject/web": "^5.0.12" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.0.12.tgz", + "integrity": "sha512-qt4jAEzQGPZ31My1gFGPzzJHJveYhVycW7RHkuX0W8fvMdg7wr0uvP7mQEptMVrb+jYwsVktCf6gBGwWDpFiTA==", "dev": true, "requires": { "@ethersproject/abstract-provider": "^5.0.8", @@ -3437,6 +3460,170 @@ "@ethersproject/logger": "^5.0.8", "@ethersproject/properties": "^5.0.7" } + }, + "@ethersproject/address": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.10.tgz", + "integrity": "sha512-70vqESmW5Srua1kMDIN6uVfdneZMaMyRYH4qPvkAXGkbicrCOsA9m01vIloA4wYiiF+HLEfL1ENKdn5jb9xiAw==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.0.13", + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/keccak256": "^5.0.7", + "@ethersproject/logger": "^5.0.8", + "@ethersproject/rlp": "^5.0.7" + } + }, + "@ethersproject/base64": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.0.8.tgz", + "integrity": "sha512-PNbpHOMgZpZ1skvQl119pV2YkCPXmZTxw+T92qX0z7zaMFPypXWTZBzim+hUceb//zx4DFjeGT4aSjZRTOYThg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.0.9" + } + }, + "@ethersproject/bignumber": { + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.14.tgz", + "integrity": "sha512-Q4TjMq9Gg3Xzj0aeJWqJgI3tdEiPiET7Y5OtNtjTAODZ2kp4y9jMNg97zVcvPedFvGROdpGDyCI77JDFodUzOw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/logger": "^5.0.8", + "bn.js": "^4.4.0" + } + }, + "@ethersproject/bytes": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.0.10.tgz", + "integrity": "sha512-vpu0v1LZ1j1s9kERQIMnVU69MyHEzUff7nqK9XuCU4vx+AM8n9lU2gj7jtJIvGSt9HzatK/6I6bWusI5nyuaTA==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.0.8" + } + }, + "@ethersproject/constants": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.0.9.tgz", + "integrity": "sha512-2uAKH89UcaJP/Sc+54u92BtJtZ4cPgcS1p0YbB1L3tlkavwNvth+kNCUplIB1Becqs7BOZr0B/3dMNjhJDy4Dg==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.0.13" + } + }, + "@ethersproject/hash": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.0.11.tgz", + "integrity": "sha512-H3KJ9fk33XWJ2djAW03IL7fg3DsDMYjO1XijiUb1hJ85vYfhvxu0OmsU7d3tg2Uv1H1kFSo8ghr3WFQ8c+NL3g==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.0.10", + "@ethersproject/address": "^5.0.9", + "@ethersproject/bignumber": "^5.0.13", + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/keccak256": "^5.0.7", + "@ethersproject/logger": "^5.0.8", + "@ethersproject/properties": "^5.0.7", + "@ethersproject/strings": "^5.0.8" + } + }, + "@ethersproject/keccak256": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.0.8.tgz", + "integrity": "sha512-zoGbwXcWWs9MX4NOAZ7N0hhgIRl4Q/IO/u9c/RHRY4WqDy3Ywm0OLamEV53QDwhjwn3YiiVwU1Ve5j7yJ0a/KQ==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.0.9", + "js-sha3": "0.5.7" + } + }, + "@ethersproject/logger": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.0.9.tgz", + "integrity": "sha512-kV3Uamv3XOH99Xf3kpIG3ZkS7mBNYcLDM00JSDtNgNB4BihuyxpQzIZPRIDmRi+95Z/R1Bb0X2kUNHa/kJoVrw==", + "dev": true + }, + "@ethersproject/networks": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.0.8.tgz", + "integrity": "sha512-PYpptlO2Tu5f/JEBI5hdlMds5k1DY1QwVbh3LKPb3un9dQA2bC51vd2/gRWAgSBpF3kkmZOj4FhD7ATLX4H+DA==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.0.8" + } + }, + "@ethersproject/properties": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.8.tgz", + "integrity": "sha512-zEnLMze2Eu2VDPj/05QwCwMKHh506gpT9PP9KPVd4dDB+5d6AcROUYVLoIIQgBYK7X/Gw0UJmG3oVtnxOQafAw==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.0.8" + } + }, + "@ethersproject/rlp": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.0.8.tgz", + "integrity": "sha512-E4wdFs8xRNJfzNHmnkC8w5fPeT4Wd1U2cust3YeT16/46iSkLT8nn8ilidC6KhR7hfuSZE4UqSPzyk76p7cdZg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/logger": "^5.0.8" + } + }, + "@ethersproject/signing-key": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.0.9.tgz", + "integrity": "sha512-AobnsEiLv+Z4a/NbbelwB/Lsnc+qxeNejXDlEwbo/nwjijvxLpwiNN+rjx/lQGel1QnQ/d+lEv7xezyUaXdKFQ==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/logger": "^5.0.8", + "@ethersproject/properties": "^5.0.7", + "elliptic": "6.5.3" + } + }, + "@ethersproject/strings": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.0.9.tgz", + "integrity": "sha512-ogxBpcUpdO524CYs841MoJHgHxEPUy0bJFDS4Ezg8My+WYVMfVAOlZSLss0Rurbeeam8CpUVDzM4zUn09SU66Q==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/constants": "^5.0.8", + "@ethersproject/logger": "^5.0.8" + } + }, + "@ethersproject/transactions": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.0.10.tgz", + "integrity": "sha512-Tqpp+vKYQyQdJQQk4M73tDzO7ODf2D42/sJOcKlDAAbdSni13v6a+31hUdo02qYXhVYwIs+ZjHnO4zKv5BNk8w==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.0.9", + "@ethersproject/bignumber": "^5.0.13", + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/constants": "^5.0.8", + "@ethersproject/keccak256": "^5.0.7", + "@ethersproject/logger": "^5.0.8", + "@ethersproject/properties": "^5.0.7", + "@ethersproject/rlp": "^5.0.7", + "@ethersproject/signing-key": "^5.0.8" + } + }, + "@ethersproject/web": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.0.13.tgz", + "integrity": "sha512-G3x/Ns7pQm21ALnWLbdBI5XkW/jrsbXXffI9hKNPHqf59mTxHYtlNiSwxdoTSwCef3Hn7uvGZpaSgTyxs7IufQ==", + "dev": true, + "requires": { + "@ethersproject/base64": "^5.0.7", + "@ethersproject/bytes": "^5.0.9", + "@ethersproject/logger": "^5.0.8", + "@ethersproject/properties": "^5.0.7", + "@ethersproject/strings": "^5.0.8" + } } } }, @@ -10072,6 +10259,16 @@ "verror": "1.10.0" } }, + "keccak": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", + "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", + "dev": true, + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", diff --git a/solidity/package.json b/solidity/package.json index 214da0971..2ba82fec6 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -3,11 +3,11 @@ "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.1", "@nomiclabs/hardhat-waffle": "^2.0.1", - "@openzeppelin/contracts": "^3.3.0", + "@openzeppelin/contracts": "^3.4.0", "@summa-tx/memview-sol": "^1.1.1", "chai": "^4.2.0", "ethereum-waffle": "^3.2.2", - "ethers": "^5.0.26", + "ethers": "^5.0.28", "hardhat": "^2.0.8", "hardhat-gas-reporter": "^1.0.4", "prettier": "2.2.1", @@ -22,7 +22,7 @@ }, "dependencies": {}, "scripts": { - "prettier": "prettier --write 'contracts/**/*.sol' ./test ./scripts ./lib", + "prettier": "prettier --write 'contracts/**/*.sol' ./test ./scripts", "compile": "hardhat compile && ./scripts/update_abis.sh", "coverage": "hardhat coverage", "test": "hardhat test" diff --git a/solidity/test/Home.test.js b/solidity/test/Home.test.js new file mode 100644 index 000000000..0807153d6 --- /dev/null +++ b/solidity/test/Home.test.js @@ -0,0 +1,132 @@ +const { waffle, ethers } = require('hardhat'); +const { provider, deployMockContract } = waffle; +const { expect } = require('chai'); +const NoSortition = require('../artifacts/contracts/Sortition.sol/NoSortition.json'); + +const ACTIVE = 0; +const FAILED = 1; +const originSLIP44 = 1234; + +describe('Home', async () => { + let home, signer, fakeSigner, updater, fakeUpdater; + + // Helper function that enqueues message and returns its root + const enqueueMessageAndGetRoot = async (message, recipient) => { + message = ethers.utils.formatBytes32String(message); + recipient = ethers.utils.formatBytes32String(recipient); + await home.enqueue(originSLIP44, recipient, message); + const [_currentRoot, latestRoot] = await home.suggestUpdate(); + return latestRoot; + }; + + before(async () => { + [signer, fakeSigner] = provider.getWallets(); + updater = await optics.Updater.fromSigner(signer, originSLIP44); + fakeUpdater = await optics.Updater.fromSigner(fakeSigner, originSLIP44); + }); + + beforeEach(async () => { + const mockSortition = await deployMockContract(signer, NoSortition.abi); + await mockSortition.mock.current.returns(signer.address); + await mockSortition.mock.slash.returns(); + + const Home = await ethers.getContractFactory('TestHome'); + home = await Home.deploy(originSLIP44, mockSortition.address); + await home.deployed(); + }); + + it('Halts on fail', async () => { + await home.setFailed(); + expect(await home.state()).to.equal(FAILED); + + const recipient = ethers.utils.formatBytes32String('recipient'); + const message = ethers.utils.formatBytes32String('message'); + await expect( + home.enqueue(originSLIP44, recipient, message), + ).to.be.revertedWith('failed state'); + }); + + it('Suggests current root and latest root on suggestUpdate', async () => { + const currentRoot = await home.current(); + + const recipient = ethers.utils.formatBytes32String('recipient'); + const message = ethers.utils.formatBytes32String('message'); + await home.enqueue(originSLIP44, recipient, message); + const latestEnqueuedRoot = await home.queueEnd(); + + const [suggestedCurrent, suggestedNew] = await home.suggestUpdate(); + expect(suggestedCurrent).to.equal(currentRoot); + expect(suggestedNew).to.equal(latestEnqueuedRoot); + }); + + it('Accepts a valid update', async () => { + const currentRoot = await home.current(); + const newRoot = await enqueueMessageAndGetRoot('message', 'recipient'); + + const { signature } = await updater.signUpdate(currentRoot, newRoot); + await expect(home.update(currentRoot, newRoot, signature)) + .to.emit(home, 'Update') + .withArgs(originSLIP44, currentRoot, newRoot, signature); + }); + + it('Rejects update that does not build off of current root', async () => { + // First root is current root + const secondRoot = await enqueueMessageAndGetRoot('message', 'recipient'); + const thirdRoot = await enqueueMessageAndGetRoot('message2', 'recipient2'); + + // Try to submit update that skips the current (first) root + const { signature } = await updater.signUpdate(secondRoot, thirdRoot); + await expect( + home.update(secondRoot, thirdRoot, signature), + ).to.be.revertedWith('not a current update'); + }); + + it('Rejects update that does not exist in queue', async () => { + const currentRoot = await home.current(); + const fakeNewRoot = ethers.utils.formatBytes32String('fake root'); + + const { signature } = await updater.signUpdate(currentRoot, fakeNewRoot); + await expect(home.update(currentRoot, fakeNewRoot, signature)).to.emit( + home, + 'ImproperUpdate', + ); + + expect(await home.state()).to.equal(FAILED); + }); + + it('Rejects update from non-updater address', async () => { + const currentRoot = await home.current(); + const newRoot = await enqueueMessageAndGetRoot('message', 'recipient'); + + const { signature: fakeSignature } = await fakeUpdater.signUpdate( + currentRoot, + newRoot, + ); + await expect( + home.update(currentRoot, newRoot, fakeSignature), + ).to.be.revertedWith('bad sig'); + }); + + it('Fails on valid double update proof', async () => { + const firstRoot = await home.current(); + const secondRoot = await enqueueMessageAndGetRoot('message', 'recipient'); + const thirdRoot = await enqueueMessageAndGetRoot('message2', 'recipient2'); + + const { signature } = await updater.signUpdate(firstRoot, secondRoot); + const { signature: signature2 } = await updater.signUpdate( + firstRoot, + thirdRoot, + ); + + await expect( + home.doubleUpdate( + firstRoot, + [secondRoot, thirdRoot], + signature, + signature2, + ), + ).to.emit(home, 'DoubleUpdate'); + + expect(await home.state()).to.equal(FAILED); + }); +});