From fe6ad65adbf39428fd482d111e6bacc24bafe9f8 Mon Sep 17 00:00:00 2001 From: wangdong Date: Sat, 21 Apr 2018 21:25:25 -0400 Subject: [PATCH 1/5] added boilerplate code for multihash --- contracts/LoopringProtocol.sol | 22 ++-------- contracts/LoopringProtocolImpl.sol | 63 ++++++----------------------- contracts/lib/BytesUtil.sol | 37 +++++++++++++++++ contracts/lib/MultihashUtil.sol | 65 ++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 68 deletions(-) create mode 100644 contracts/lib/BytesUtil.sol create mode 100644 contracts/lib/MultihashUtil.sol diff --git a/contracts/LoopringProtocol.sol b/contracts/LoopringProtocol.sol index 8bc1d4c1..fb502e83 100644 --- a/contracts/LoopringProtocol.sol +++ b/contracts/LoopringProtocol.sol @@ -67,16 +67,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; @@ -111,15 +107,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 @@ -128,9 +116,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 a2a86578..7e727eef 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, @@ -350,9 +341,7 @@ contract LoopringProtocolImpl is LoopringProtocol { ); 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(sigSize == ctx.sigList.length, "invalid signature size"); } /// @dev Assemble input data into structs so we can pass them to other functions. @@ -399,13 +388,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 +451,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] ); } } @@ -937,28 +922,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..cf474fba --- /dev/null +++ b/contracts/lib/BytesUtil.sol @@ -0,0 +1,37 @@ +/* + + 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) + { + for (uint i = 0; i < 32; i++) { + out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); + } + } +} \ No newline at end of file diff --git a/contracts/lib/MultihashUtil.sol b/contracts/lib/MultihashUtil.sol new file mode 100644 index 00000000..ff4e1a13 --- /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 { + + 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"); + + uint8 v = uint8(multihash[2]); + bytes32 r = BytesUtil.bytesToBytes32(multihash, 3); + bytes32 s = BytesUtil.bytesToBytes32(multihash, 11); + + require( + signer == ecrecover( + keccak256("\x19Ethereum Signed Message:\n32", plaintext), + v, + r, + s + ), + "bad signature" + ); + } else { + require(false, "unsupported algorithm"); + } + } +} \ No newline at end of file From ab9841335d70e41f69f9f306f36e960f542340ff Mon Sep 17 00:00:00 2001 From: wangdong Date: Sat, 21 Apr 2018 21:28:11 -0400 Subject: [PATCH 2/5] minor --- contracts/LoopringProtocolImpl.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/LoopringProtocolImpl.sol b/contracts/LoopringProtocolImpl.sol index 7e727eef..5c5d9aaa 100644 --- a/contracts/LoopringProtocolImpl.sol +++ b/contracts/LoopringProtocolImpl.sol @@ -340,8 +340,10 @@ contract LoopringProtocolImpl is LoopringProtocol { "invalid ring size" ); - uint sigSize = ctx.ringSize << 1; - require(sigSize == ctx.sigList.length, "invalid signature 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. From aa37577f4c6b6f75f3c5732ad97a8414dd494b6e Mon Sep 17 00:00:00 2001 From: wangdong Date: Sun, 22 Apr 2018 23:23:43 -0400 Subject: [PATCH 3/5] remove ringhash from RingMined event --- contracts/LoopringProtocol.sol | 3 +-- contracts/LoopringProtocolImpl.sol | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/LoopringProtocol.sol b/contracts/LoopringProtocol.sol index fb502e83..e8cc28ed 100644 --- a/contracts/LoopringProtocol.sol +++ b/contracts/LoopringProtocol.sol @@ -38,8 +38,7 @@ contract LoopringProtocol { event RingMined( uint _ringIndex, - bytes32 indexed _ringHash, - address _miner, + address indexed _miner, Fill[] _fills ); diff --git a/contracts/LoopringProtocolImpl.sol b/contracts/LoopringProtocolImpl.sol index 5c5d9aaa..41b493a4 100644 --- a/contracts/LoopringProtocolImpl.sol +++ b/contracts/LoopringProtocolImpl.sol @@ -792,7 +792,6 @@ contract LoopringProtocolImpl is LoopringProtocol { emit RingMined( ctx.ringIndex, - ctx.ringHash, ctx.miner, fills ); From 103177765e0a74f9acff225182a4310266e4189c Mon Sep 17 00:00:00 2001 From: Brechtpd Date: Mon, 23 Apr 2018 06:25:19 +0200 Subject: [PATCH 4/5] Optimized bytesToBytes32() (#321) --- contracts/lib/BytesUtil.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/lib/BytesUtil.sol b/contracts/lib/BytesUtil.sol index cf474fba..5c3ec22f 100644 --- a/contracts/lib/BytesUtil.sol +++ b/contracts/lib/BytesUtil.sol @@ -30,8 +30,11 @@ library BytesUtil { pure returns (bytes32 out) { - for (uint i = 0; i < 32; i++) { - out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); + require(b.length >= offset + 32); + bytes32 temp; + assembly { + temp := mload(add(add(b, 0x20), offset)) } + return temp; } -} \ No newline at end of file +} From a9dd4d471833bd49e9b28414952b56cdeee81617 Mon Sep 17 00:00:00 2001 From: wangdong Date: Mon, 23 Apr 2018 00:33:31 -0400 Subject: [PATCH 5/5] minor improvement --- contracts/lib/MultihashUtil.sol | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/lib/MultihashUtil.sol b/contracts/lib/MultihashUtil.sol index ff4e1a13..029096af 100644 --- a/contracts/lib/MultihashUtil.sol +++ b/contracts/lib/MultihashUtil.sol @@ -29,6 +29,8 @@ import "./BytesUtil.sol"; /// - https://github.com/multiformats/js-multihash library MultihashUtil { + string public constant SIG_PREFIX = "\x19Ethereum Signed Message:\n32"; + function verifySignature( address signer, bytes32 plaintext, @@ -44,22 +46,20 @@ library MultihashUtil { // Currently we default 0 to be the Ethereum's default signature. if (algorithm == 0) { require(size == 65, "bad multihash size"); - - uint8 v = uint8(multihash[2]); - bytes32 r = BytesUtil.bytesToBytes32(multihash, 3); - bytes32 s = BytesUtil.bytesToBytes32(multihash, 11); - require( signer == ecrecover( - keccak256("\x19Ethereum Signed Message:\n32", plaintext), - v, - r, - s + keccak256( + SIG_PREFIX, + plaintext + ), + uint8(multihash[2]), + BytesUtil.bytesToBytes32(multihash, 3), + BytesUtil.bytesToBytes32(multihash, 11) ), "bad signature" ); } else { - require(false, "unsupported algorithm"); + revert("unsupported algorithm"); } } } \ No newline at end of file