From 5937a788fbb30214c54086fc3aa6779b4224d947 Mon Sep 17 00:00:00 2001 From: Ted Wu Date: Mon, 22 Feb 2021 21:33:39 -0800 Subject: [PATCH 1/6] lint --- contracts/_external/ECDSA.sol | 98 +++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 contracts/_external/ECDSA.sol diff --git a/contracts/_external/ECDSA.sol b/contracts/_external/ECDSA.sol new file mode 100644 index 00000000..e48c4437 --- /dev/null +++ b/contracts/_external/ECDSA.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.7.6; + +/** + * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. + * + * These functions can be used to verify that a message was signed by the holder + * of the private keys of a given address. + */ +library ECDSA { + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature`. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + */ + function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { + // Check the signature length + if (signature.length != 65) { + revert("ECDSA: invalid signature length"); + } + + // Divide the signature in r, s and v variables + bytes32 r; + bytes32 s; + uint8 v; + + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + // solhint-disable-next-line no-inline-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + + return recover(hash, v, r, s); + } + + /** + * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function recover( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and + // make the signature unique. Appendix F in the Ethereum Yellow paper + // (https://ethereum.github.io/yellowpaper/paper.pdf), defines the valid range + // for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the + // lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, + // calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and + // flip v from 27 to 28 or vice versa. If your library also generates signatures with 0/1 + // for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + require( + uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + "ECDSA: invalid signature 's' value" + ); + require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(hash, v, r, s); + require(signer != address(0), "ECDSA: invalid signature"); + + return signer; + } + + /** + * @dev Returns an Ethereum Signed Message, created from a `hash`. This + * replicates the behavior of the + * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] + * JSON-RPC method. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { + // 32 is the length in bytes of hash, + // enforced by the type signature above + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } +} From 0f8cf4030a437f50f92e38645cc800217cc76337 Mon Sep 17 00:00:00 2001 From: Ted Wu Date: Mon, 22 Feb 2021 22:03:06 -0800 Subject: [PATCH 2/6] use the ecdsa functions --- contracts/Orchestrator.sol | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/contracts/Orchestrator.sol b/contracts/Orchestrator.sol index 801407ed..b8b5045c 100644 --- a/contracts/Orchestrator.sol +++ b/contracts/Orchestrator.sol @@ -1,6 +1,7 @@ pragma solidity 0.7.6; import "./_external/Ownable.sol"; +import "./_external/ECDSA.sol"; import "./UFragmentsPolicy.sol"; @@ -21,6 +22,8 @@ contract Orchestrator is Ownable { UFragmentsPolicy public policy; + ECDSA public ecdsa; + /** * @param policy_ Address of the UFragments policy. */ @@ -53,6 +56,22 @@ contract Orchestrator is Ownable { } } + /** + * @notice Based on the hashed message and the signature (which is composed of v, r, s), + * erecover can return the address of the signer and since only EOAs can create valid + * signatures, this guarantees that the beneficiary address is not a contract but an EOA + * @param hash hashed message + * @param v {27 or 28} + * @param r 32 bytes of the first half of signature + * @param s 32 bytes of the second half of the signature + */ + function verifyAddressOfSignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns(bool) { + bytes32 prefixedHash = ecdsa.toEthSignedMessageHash(hash); + + return ecdsa.recover(prefixedHash, v, r, s) == msg.sender; + } + + /** * @notice Adds a transaction that gets called for a downstream receiver of rebases * @param destination Address of contract destination From 3f2abea68581bca4d408848b664f7701eede9168 Mon Sep 17 00:00:00 2001 From: Ted Wu Date: Tue, 23 Feb 2021 21:18:33 -0800 Subject: [PATCH 3/6] fix compiler error --- contracts/Orchestrator.sol | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contracts/Orchestrator.sol b/contracts/Orchestrator.sol index b8b5045c..819fa9c5 100644 --- a/contracts/Orchestrator.sol +++ b/contracts/Orchestrator.sol @@ -1,7 +1,7 @@ pragma solidity 0.7.6; import "./_external/Ownable.sol"; -import "./_external/ECDSA.sol"; +import { ECDSA } from "./_external/ECDSA.sol"; import "./UFragmentsPolicy.sol"; @@ -65,13 +65,17 @@ contract Orchestrator is Ownable { * @param r 32 bytes of the first half of signature * @param s 32 bytes of the second half of the signature */ - function verifyAddressOfSignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns(bool) { + function verifyAddressOfSignature( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal returns (bool) { bytes32 prefixedHash = ecdsa.toEthSignedMessageHash(hash); return ecdsa.recover(prefixedHash, v, r, s) == msg.sender; } - /** * @notice Adds a transaction that gets called for a downstream receiver of rebases * @param destination Address of contract destination From 28e35fd3d7b2575f21c57eff330f9ff6e5f3d49d Mon Sep 17 00:00:00 2001 From: Ted Wu Date: Tue, 23 Feb 2021 21:20:29 -0800 Subject: [PATCH 4/6] add using library --- contracts/Orchestrator.sol | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/Orchestrator.sol b/contracts/Orchestrator.sol index 819fa9c5..ffd11c96 100644 --- a/contracts/Orchestrator.sol +++ b/contracts/Orchestrator.sol @@ -1,9 +1,9 @@ pragma solidity 0.7.6; import "./_external/Ownable.sol"; -import { ECDSA } from "./_external/ECDSA.sol"; import "./UFragmentsPolicy.sol"; +import { ECDSA } from "./_external/ECDSA.sol"; /** * @title Orchestrator @@ -22,7 +22,8 @@ contract Orchestrator is Ownable { UFragmentsPolicy public policy; - ECDSA public ecdsa; + // using ECDSA library to return an address + using ECDSA for address; /** * @param policy_ Address of the UFragments policy. @@ -71,9 +72,9 @@ contract Orchestrator is Ownable { bytes32 r, bytes32 s ) internal returns (bool) { - bytes32 prefixedHash = ecdsa.toEthSignedMessageHash(hash); + bytes32 prefixedHash = ECDSA.toEthSignedMessageHash(hash); - return ecdsa.recover(prefixedHash, v, r, s) == msg.sender; + return ECDSA.recover(prefixedHash, v, r, s) == msg.sender; } /** From 478ed9f434a0ce97f7a4e1be49c57d59d39e18b0 Mon Sep 17 00:00:00 2001 From: Ted Wu Date: Wed, 24 Feb 2021 17:54:17 -0800 Subject: [PATCH 5/6] import ecdsa --- contracts/Orchestrator.sol | 8 +-- contracts/_external/ECDSA.sol | 98 ----------------------------------- 2 files changed, 4 insertions(+), 102 deletions(-) delete mode 100644 contracts/_external/ECDSA.sol diff --git a/contracts/Orchestrator.sol b/contracts/Orchestrator.sol index ffd11c96..0da45400 100644 --- a/contracts/Orchestrator.sol +++ b/contracts/Orchestrator.sol @@ -3,7 +3,8 @@ pragma solidity 0.7.6; import "./_external/Ownable.sol"; import "./UFragmentsPolicy.sol"; -import { ECDSA } from "./_external/ECDSA.sol"; + +import "openzeppelin/contracts/utils/cryptography/ECDSA.sol"; /** * @title Orchestrator @@ -11,6 +12,8 @@ import { ECDSA } from "./_external/ECDSA.sol"; * actions with external consumers. */ contract Orchestrator is Ownable { + using ECDSA for address; + struct Transaction { bool enabled; address destination; @@ -22,9 +25,6 @@ contract Orchestrator is Ownable { UFragmentsPolicy public policy; - // using ECDSA library to return an address - using ECDSA for address; - /** * @param policy_ Address of the UFragments policy. */ diff --git a/contracts/_external/ECDSA.sol b/contracts/_external/ECDSA.sol deleted file mode 100644 index e48c4437..00000000 --- a/contracts/_external/ECDSA.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.7.6; - -/** - * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. - * - * These functions can be used to verify that a message was signed by the holder - * of the private keys of a given address. - */ -library ECDSA { - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature`. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - */ - function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { - // Check the signature length - if (signature.length != 65) { - revert("ECDSA: invalid signature length"); - } - - // Divide the signature in r, s and v variables - bytes32 r; - bytes32 s; - uint8 v; - - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - // solhint-disable-next-line no-inline-assembly - assembly { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - } - - return recover(hash, v, r, s); - } - - /** - * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`, - * `r` and `s` signature fields separately. - */ - function recover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address) { - // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and - // make the signature unique. Appendix F in the Ethereum Yellow paper - // (https://ethereum.github.io/yellowpaper/paper.pdf), defines the valid range - // for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most - // signatures from current libraries generate a unique signature with an s-value in the - // lower half order. - // - // If your library generates malleable signatures, such as s-values in the upper range, - // calculate a new s-value - // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and - // flip v from 27 to 28 or vice versa. If your library also generates signatures with 0/1 - // for v instead 27/28, add 27 to v to accept - // these malleable signatures as well. - require( - uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, - "ECDSA: invalid signature 's' value" - ); - require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); - - // If the signature is valid (and not malleable), return the signer address - address signer = ecrecover(hash, v, r, s); - require(signer != address(0), "ECDSA: invalid signature"); - - return signer; - } - - /** - * @dev Returns an Ethereum Signed Message, created from a `hash`. This - * replicates the behavior of the - * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] - * JSON-RPC method. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { - // 32 is the length in bytes of hash, - // enforced by the type signature above - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } -} From f809bcc1c9e3091b334e1c61edbf9c38316e3de0 Mon Sep 17 00:00:00 2001 From: Ted Wu Date: Wed, 24 Feb 2021 21:19:30 -0800 Subject: [PATCH 6/6] Added openzeppelin contracts --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index 4efbf8b1..dca559f2 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "typescript": "^4.0.2" }, "dependencies": { + "@openzeppelin/contracts": "^3.4.0", "hardhat-gas-reporter": "^1.0.4" } } diff --git a/yarn.lock b/yarn.lock index d4b386ab..09b1b778 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1040,6 +1040,11 @@ "@types/sinon-chai" "^3.2.3" "@types/web3" "1.0.19" +"@openzeppelin/contracts@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.0.tgz#9a1669ad5f9fdfb6e273bb5a4fed10cb4cc35eb0" + integrity sha512-qh+EiHWzfY/9CORr+eRUkeEUP1WiFUcq3974bLHwyYzLBUtK6HPaMkIUHi74S1rDTZ0sNz42DwPc5A4IJvN3rg== + "@openzeppelin/hardhat-upgrades@^1.4.3": version "1.4.3" resolved "https://registry.yarnpkg.com/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.4.3.tgz#dfd0f7668cbc343455c2a6b2d3ba4d68034e5dc2"