diff --git a/contracts/LoopringProtocol.sol b/contracts/LoopringProtocol.sol index 8d6323f9..e8cc28ed 100644 --- a/contracts/LoopringProtocol.sol +++ b/contracts/LoopringProtocol.sol @@ -66,16 +66,12 @@ contract LoopringProtocol { /// validUntil (second), lrcFee, and cancelAmount. /// @param option This indicates when a order should be considered /// as 'completely filled'. - /// @param v Order ECDSA signature parameter v. - /// @param r Order ECDSA signature parameters r. - /// @param s Order ECDSA signature parameters s. + /// @param sig Order's signature. function cancelOrder( address[6] addresses, uint[6] values, uint8 option, - uint8 v, - bytes32 r, - bytes32 s + bytes sig ) external; @@ -110,15 +106,7 @@ contract LoopringProtocol { /// amountS, amountB, validSince (second), /// validUntil (second), lrcFee, and rateAmountS. /// @param optionList This indicates when a order should be considered - /// @param vList List of v for each order. This list is 1-larger than - /// the previous lists, with the last element being the - /// v value of the ring signature. - /// @param rList List of r for each order. This list is 1-larger than - /// the previous lists, with the last element being the - /// r value of the ring signature. - /// @param sList List of s for each order. This list is 1-larger than - /// the previous lists, with the last element being the - /// s value of the ring signature. + /// @param sigList Signature lists. /// @param miner Miner address. /// @param feeSelections - /// Bits to indicate fee selections. `1` represents margin @@ -127,9 +115,7 @@ contract LoopringProtocol { address[5][] addressesList, uint[6][] valuesList, bool[] optionList, - uint8[] vList, - bytes32[] rList, - bytes32[] sList, + bytes[] sigList, address miner, uint8 feeSelections ) diff --git a/contracts/LoopringProtocolImpl.sol b/contracts/LoopringProtocolImpl.sol index 6f70757c..19da2945 100644 --- a/contracts/LoopringProtocolImpl.sol +++ b/contracts/LoopringProtocolImpl.sol @@ -21,6 +21,7 @@ pragma experimental "ABIEncoderV2"; import "./lib/AddressUtil.sol"; import "./lib/ERC20.sol"; import "./lib/MathUint.sol"; +import "./lib/MultihashUtil.sol"; import "./BrokerRegistry.sol"; import "./BrokerTracker.sol"; import "./LoopringProtocol.sol"; @@ -96,9 +97,7 @@ contract LoopringProtocolImpl is LoopringProtocol { address[5][] addressesList; uint[6][] valuesList; uint8[] optionList; - uint8[] vList; - bytes32[] rList; - bytes32[] sList; + bytes[] sigList; address miner; uint8 feeSelections; uint64 ringIndex; @@ -144,9 +143,7 @@ contract LoopringProtocolImpl is LoopringProtocol { address[6] addresses, uint[6] orderValues, uint8 option, - uint8 v, - bytes32 r, - bytes32 s + bytes sig ) external { @@ -185,12 +182,10 @@ contract LoopringProtocolImpl is LoopringProtocol { bytes32 orderHash = calculateOrderHash(order); - verifySignature( + MultihashUtil.verifySignature( order.signer, orderHash, - v, - r, - s + sig ); if (order.signer != order.owner) { @@ -258,9 +253,7 @@ contract LoopringProtocolImpl is LoopringProtocol { address[5][] addressesList, uint[6][] valuesList, uint8[] optionList, - uint8[] vList, - bytes32[] rList, - bytes32[] sList, + bytes[] sigList, address miner, uint8 feeSelections ) @@ -270,9 +263,7 @@ contract LoopringProtocolImpl is LoopringProtocol { addressesList, valuesList, optionList, - vList, - rList, - sList, + sigList, miner, feeSelections, ringIndex, @@ -349,10 +340,10 @@ contract LoopringProtocolImpl is LoopringProtocol { "invalid ring size" ); - uint sigSize = ctx.ringSize << 1; - require(sigSize == ctx.vList.length, "invalid vList size"); - require(sigSize == ctx.rList.length, "invalid rList size"); - require(sigSize == ctx.sList.length, "invalid sList size"); + require( + (ctx.ringSize << 1) == ctx.sigList.length, + "invalid signature size" + ); } /// @dev Assemble input data into structs so we can pass them to other functions. @@ -399,13 +390,11 @@ contract LoopringProtocolImpl is LoopringProtocol { order.orderHash = calculateOrderHash(order); - verifySignature( + MultihashUtil.verifySignature( order.signer, order.orderHash, - ctx.vList[i], - ctx.rList[i], - ctx.sList[i] - ); + ctx.sigList[i] + ); if (order.signer != order.owner) { BrokerRegistry brokerRegistry = BrokerRegistry(brokerRegistryAddress); @@ -464,12 +453,10 @@ contract LoopringProtocolImpl is LoopringProtocol { for (uint i = 0; i < ctx.ringSize; i++) { j = i + ctx.ringSize; - verifySignature( + MultihashUtil.verifySignature( ctx.orders[i].authAddr, ctx.ringHash, - ctx.vList[j], - ctx.rList[j], - ctx.sList[j] + ctx.sigList[i] ); } } @@ -927,28 +914,6 @@ contract LoopringProtocolImpl is LoopringProtocol { ); } - /// @dev Verify signer's signature. - function verifySignature( - address signer, - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) - private - pure - { - require( - signer == ecrecover( - keccak256("\x19Ethereum Signed Message:\n32", hash), - v, - r, - s - ), - "bad signature" - ); - } - function getTradingPairCutoffs( address orderOwner, address token1, diff --git a/contracts/lib/BytesUtil.sol b/contracts/lib/BytesUtil.sol new file mode 100644 index 00000000..5c3ec22f --- /dev/null +++ b/contracts/lib/BytesUtil.sol @@ -0,0 +1,40 @@ +/* + + Copyright 2017 Loopring Project Ltd (Loopring Foundation). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +pragma solidity 0.4.23; +pragma experimental "v0.5.0"; +pragma experimental "ABIEncoderV2"; + + +/// @title Utility Functions for bytes +/// @author Daniel Wang - +library BytesUtil { + function bytesToBytes32( + bytes b, + uint offset + ) + public + pure + returns (bytes32 out) + { + require(b.length >= offset + 32); + bytes32 temp; + assembly { + temp := mload(add(add(b, 0x20), offset)) + } + return temp; + } +} diff --git a/contracts/lib/MultihashUtil.sol b/contracts/lib/MultihashUtil.sol new file mode 100644 index 00000000..029096af --- /dev/null +++ b/contracts/lib/MultihashUtil.sol @@ -0,0 +1,65 @@ +/* + + Copyright 2017 Loopring Project Ltd (Loopring Foundation). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +pragma solidity 0.4.23; +pragma experimental "v0.5.0"; +pragma experimental "ABIEncoderV2"; + +import "./BytesUtil.sol"; + + +/// @title Utility Functions for Multihash signature verificaiton +/// @author Daniel Wang - +/// For more information: +/// - https://github.com/saurfang/ipfs-multihash-on-solidity +/// - https://github.com/multiformats/multihash +/// - https://github.com/multiformats/js-multihash +library MultihashUtil { + + string public constant SIG_PREFIX = "\x19Ethereum Signed Message:\n32"; + + function verifySignature( + address signer, + bytes32 plaintext, + bytes multihash + ) + public + pure + { + uint8 algorithm = uint8(multihash[0]); + uint8 size = uint8(multihash[1]); + require(multihash.length == (2 + size), "bad multihash size"); + + // Currently we default 0 to be the Ethereum's default signature. + if (algorithm == 0) { + require(size == 65, "bad multihash size"); + require( + signer == ecrecover( + keccak256( + SIG_PREFIX, + plaintext + ), + uint8(multihash[2]), + BytesUtil.bytesToBytes32(multihash, 3), + BytesUtil.bytesToBytes32(multihash, 11) + ), + "bad signature" + ); + } else { + revert("unsupported algorithm"); + } + } +} \ No newline at end of file