From 2343c9f558d44a9e1aeb9b679721ebdaaa1f01d5 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 08:27:59 +0000 Subject: [PATCH 01/19] Added ECDSA library --- .gas-snapshot | 17 ++++++ src/test/ECDSA.t.sol | 126 +++++++++++++++++++++++++++++++++++++++++++ src/utils/ECDSA.sol | 96 +++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 src/test/ECDSA.t.sol create mode 100644 src/utils/ECDSA.sol diff --git a/.gas-snapshot b/.gas-snapshot index d61fb9d3..41895e65 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -23,6 +23,23 @@ DSTestPlusTest:testBrutalizeMemory() (gas: 823) DSTestPlusTest:testFailBoundMinBiggerThanMax() (gas: 287) DSTestPlusTest:testMeasuringGas() (gas: 24600) DSTestPlusTest:testRelApproxEqBothZeroesPasses() (gas: 391) +EDCSATest:testBytes32ToEthSignedMessageHash() (gas: 342) +EDCSATest:testBytesToEthSignedMessageHashEmpty() (gas: 599) +EDCSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 970) +EDCSATest:testBytesToEthSignedMessageHashShort() (gas: 688) +EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1890) +EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1764) +EDCSATest:testRecoverWithInvalidSignature() (gas: 5334) +EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 5138) +EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1914) +EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5233) +EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1913) +EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5160) +EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1912) +EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5213) +EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1890) +EDCSATest:testRecoverWithValidSignature() (gas: 5309) +EDCSATest:testRecoverWithWrongSigner() (gas: 5289) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/test/ECDSA.t.sol b/src/test/ECDSA.t.sol new file mode 100644 index 00000000..4b33fb0c --- /dev/null +++ b/src/test/ECDSA.t.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {ECDSA} from "../utils/ECDSA.sol"; + +contract EDCSATest is DSTestPlus { + using ECDSA for bytes32; + using ECDSA for bytes; + + bytes32 constant TEST_MESSAGE = 0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d; + + bytes32 constant WRONG_MESSAGE = 0x2d0828dd7c97cff316356da3c16c68ba2316886a0e05ebafb8291939310d51a3; + + address constant SIGNER = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; + + address constant V0_SIGNER = 0x2cc1166f6212628A0deEf2B33BEFB2187D35b86c; + + address constant V1_SIGNER = 0x1E318623aB09Fe6de3C9b8672098464Aeda9100E; + + function testRecoverWithInvalidShortSignature() public { + bytes memory signature = hex"1234"; + assertTrue(this.recover(TEST_MESSAGE, signature) == address(0)); + } + + function testRecoverWithInvalidLongSignature() public { + // prettier-ignore + bytes memory signature = hex"01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; + assertTrue(this.recover(TEST_MESSAGE, signature) == address(0)); + } + + function testRecoverWithValidSignature() public { + // prettier-ignore + bytes memory signature = hex"8688e590483917863a35ef230c0f839be8418aa4ee765228eddfcea7fe2652815db01c2c84b0ec746e1b74d97475c599b3d3419fa7181b4e01de62c02b721aea1b"; + assertTrue(this.recover(TEST_MESSAGE.toEthSignedMessageHash(), signature) == SIGNER); + } + + function testRecoverWithWrongSigner() public { + // prettier-ignore + bytes memory signature = hex"8688e590483917863a35ef230c0f839be8418aa4ee765228eddfcea7fe2652815db01c2c84b0ec746e1b74d97475c599b3d3419fa7181b4e01de62c02b721aea1b"; + assertTrue(this.recover(WRONG_MESSAGE.toEthSignedMessageHash(), signature) != SIGNER); + } + + function testRecoverWithInvalidSignature() public { + // prettier-ignore + bytes memory signature = hex"332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c"; + assertTrue(this.recover(TEST_MESSAGE.toEthSignedMessageHash(), signature) != SIGNER); + } + + function testRecoverWithV0SignatureWithVersion00() public { + // prettier-ignore + bytes memory signature = hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89200"; + assertTrue(this.recover(TEST_MESSAGE, signature) == address(0)); + } + + function testRecoverWithV0SignatureWithVersion27() public { + // prettier-ignore + bytes memory signature = hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be8921b"; + assertTrue(this.recover(TEST_MESSAGE, signature) == V0_SIGNER); + } + + function testRecoverWithV0SignatureWithWrongVersion() public { + // prettier-ignore + bytes memory signature = hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89202"; + assertTrue(this.recover(TEST_MESSAGE, signature) == address(0)); + } + + function testRecoverWithV0SignatureWithShortEIP2098Format() public { + // prettier-ignore + bytes memory signature = hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892"; + assertTrue(this.recover(TEST_MESSAGE, signature) == V0_SIGNER); + } + + function testRecoverWithV1SignatureWithVersion01() public { + // prettier-ignore + bytes memory signature = hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e001"; + assertTrue(this.recover(TEST_MESSAGE, signature) == address(0)); + } + + function testRecoverWithV1SignatureWithVersion28() public { + // prettier-ignore + bytes memory signature = hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c"; + assertTrue(this.recover(TEST_MESSAGE, signature) == V1_SIGNER); + } + + function testRecoverWithV1SignatureWithWrongVersion() public { + // prettier-ignore + bytes memory signature = hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e002"; + assertTrue(this.recover(TEST_MESSAGE, signature) == address(0)); + } + + function testRecoverWithV1SignatureWithShortEIP2098Format() public { + // prettier-ignore + bytes memory signature = hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feffc8e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0"; + assertTrue(this.recover(TEST_MESSAGE, signature) == V1_SIGNER); + } + + function testBytes32ToEthSignedMessageHash() public { + // prettier-ignore + assertTrue(TEST_MESSAGE.toEthSignedMessageHash() == bytes32(0x7d768af957ef8cbf6219a37e743d5546d911dae3e46449d8a5810522db2ef65e)); + } + + function testBytesToEthSignedMessageHashShort() public { + bytes memory message = hex"61626364"; + // prettier-ignore + assertTrue(message.toEthSignedMessageHash() == bytes32(0xefd0b51a9c4e5f3449f4eeacb195bf48659fbc00d2f4001bf4c088ba0779fb33)); + } + + function testBytesToEthSignedMessageHashEmpty() public { + bytes memory message = hex""; + // prettier-ignore + assertTrue(message.toEthSignedMessageHash() == bytes32(0x5f35dce98ba4fba25530a026ed80b2cecdaa31091ba4958b99b52ea1d068adad)); + } + + function testBytesToEthSignedMessageHashEmptyLong() public { + // prettier-ignore + bytes memory message = hex"4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a3031323334353637383921402324255e262a28292d3d5b5d7b7d"; + // prettier-ignore + assertTrue(message.toEthSignedMessageHash() == bytes32(0xa46dbedd405cff161b6e80c17c8567597621d9f4c087204201097cb34448e71b)); + } + + function recover(bytes32 hash, bytes calldata signature) external pure returns (address) { + return ECDSA.recover(hash, signature); + } +} diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol new file mode 100644 index 00000000..42f52182 --- /dev/null +++ b/src/utils/ECDSA.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/MerkleProof.sol) +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol) +library ECDSA { + function recover(bytes32 hash, bytes calldata signature) internal pure returns (address result) { + bytes32 r; + bytes32 s; + uint8 v; + bool isValid; + assembly { + // Directly load the fields from the calldata. + r := calldataload(signature.offset) + s := calldataload(add(signature.offset, 0x20)) + switch signature.length + case 65 { + v := byte(0, calldataload(add(signature.offset, 0x40))) + } + case 64 { + // Here, `s` is actually `vs` that needs to be recovered into `v` and `s`. + v := add(shr(255, s), 27) + // prettier-ignore + s := and(s, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + } + // Ensure signature is valid and not malleable. + isValid := and( + // `s` in lower half order. + // prettier-ignore + lt(s, 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1), + // `v` is 27 or 28 + byte(v, 0x0101000000) + ) + } + if (isValid) { + // If invalid, the result will be the zero address. + result = ecrecover(hash, v, r, s); + } + } + + function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { + assembly { + // Store into scratch space for keccak256. + mstore(0x20, hash) + mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") + // 0x40 - 0x04 = 0x3c + result := keccak256(0x04, 0x3c) + } + } + + function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { + assembly { + let ptr := add(mload(0x40), 128) + + let mid := ptr + let sLength := mload(s) + let end := add(mid, sLength) + + // Update the free memory pointer to allocate. + mstore(0x40, shl(5, add(1, shr(5, end)))) + + // Convert the length of the bytes to ASCII decimal representation + // and concatenate to the signature. + for { + let temp := sLength + ptr := sub(ptr, 1) + mstore8(ptr, add(48, mod(temp, 10))) + temp := div(temp, 10) + } temp { + temp := div(temp, 10) + } { + ptr := sub(ptr, 1) + mstore8(ptr, add(48, mod(temp, 10))) + } + + // Move the pointer 32 bytes lower to make room for the prefix. + let start := sub(ptr, 32) + // Concatenate the prefix to the signature. + mstore(start, "\x00\x00\x00\x00\x00\x00\x19Ethereum Signed Message:\n") + start := add(start, 6) + + // Concatenate the bytes to the signature. + for { + let temp := add(s, 0x20) + ptr := mid + } lt(ptr, end) { + ptr := add(ptr, 0x20) + } { + mstore(ptr, mload(temp)) + temp := add(temp, 0x20) + } + result := keccak256(start, sub(end, start)) + } + } +} From fcf15a4e4ac3702ca61f4f8d5f9091909ef4ebe6 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 09:13:36 +0000 Subject: [PATCH 02/19] Add comments --- .gas-snapshot | 6 +++--- src/utils/ECDSA.sol | 42 ++++++++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 41895e65..7b331e6a 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -24,9 +24,9 @@ DSTestPlusTest:testFailBoundMinBiggerThanMax() (gas: 287) DSTestPlusTest:testMeasuringGas() (gas: 24600) DSTestPlusTest:testRelApproxEqBothZeroesPasses() (gas: 391) EDCSATest:testBytes32ToEthSignedMessageHash() (gas: 342) -EDCSATest:testBytesToEthSignedMessageHashEmpty() (gas: 599) -EDCSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 970) -EDCSATest:testBytesToEthSignedMessageHashShort() (gas: 688) +EDCSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) +EDCSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) +EDCSATest:testBytesToEthSignedMessageHashShort() (gas: 629) EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1890) EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1764) EDCSATest:testRecoverWithInvalidSignature() (gas: 5334) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 42f52182..ef637119 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -51,17 +51,26 @@ library ECDSA { function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { assembly { - let ptr := add(mload(0x40), 128) + // We need at most 128 bytes for Ethereum signed message header. + // The max length of the ASCII reprenstation of a uint256 is 78 bytes. + // The length of "\x19Ethereum Signed Message:\n" is 26 bytes. + // The next multiple of 32 above 78 + 26 is 128. - let mid := ptr + // Instead of allocating, we temporarily copy the 128 bytes before the + // start of `s` data to some variables. + let m3 := mload(sub(s, 0x60)) + let m2 := mload(sub(s, 0x40)) + let m1 := mload(sub(s, 0x20)) + // The length of `s` is in bytes. let sLength := mload(s) - let end := add(mid, sLength) - // Update the free memory pointer to allocate. - mstore(0x40, shl(5, add(1, shr(5, end)))) + let ptr := add(s, 0x20) + + // `end` marks the end of the memory which we will compute the keccak256 of. + let end := add(ptr, sLength) // Convert the length of the bytes to ASCII decimal representation - // and concatenate to the signature. + // while copying it over the the memory. for { let temp := sLength ptr := sub(ptr, 1) @@ -74,23 +83,20 @@ library ECDSA { mstore8(ptr, add(48, mod(temp, 10))) } - // Move the pointer 32 bytes lower to make room for the prefix. + // Move the pointer 32 bytes lower to make room for the string. + // `start` marks the start of the memory which we will compute the keccak256 of. let start := sub(ptr, 32) - // Concatenate the prefix to the signature. + // Copy the header over to the memory. mstore(start, "\x00\x00\x00\x00\x00\x00\x19Ethereum Signed Message:\n") start := add(start, 6) - // Concatenate the bytes to the signature. - for { - let temp := add(s, 0x20) - ptr := mid - } lt(ptr, end) { - ptr := add(ptr, 0x20) - } { - mstore(ptr, mload(temp)) - temp := add(temp, 0x20) - } + // Compute the keccak256 of the memory. result := keccak256(start, sub(end, start)) + + mstore(s, sLength) + mstore(sub(s, 0x20), m1) + mstore(sub(s, 0x40), m2) + mstore(sub(s, 0x60), m3) } } } From 1132908f71fc96a3cbae02d4fdaa08672d5e2982 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 09:25:45 +0000 Subject: [PATCH 03/19] Edit comments --- src/utils/ECDSA.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index ef637119..ad4edd72 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; -/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree. +/// @notice Gas optimized ECDSA wrapper. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/MerkleProof.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol) library ECDSA { @@ -55,9 +55,9 @@ library ECDSA { // The max length of the ASCII reprenstation of a uint256 is 78 bytes. // The length of "\x19Ethereum Signed Message:\n" is 26 bytes. // The next multiple of 32 above 78 + 26 is 128. - + // Instead of allocating, we temporarily copy the 128 bytes before the - // start of `s` data to some variables. + // start of `s` data to some variables. let m3 := mload(sub(s, 0x60)) let m2 := mload(sub(s, 0x40)) let m1 := mload(sub(s, 0x20)) @@ -68,13 +68,13 @@ library ECDSA { // `end` marks the end of the memory which we will compute the keccak256 of. let end := add(ptr, sLength) - + // Convert the length of the bytes to ASCII decimal representation // while copying it over the the memory. for { let temp := sLength ptr := sub(ptr, 1) - mstore8(ptr, add(48, mod(temp, 10))) + mstore8(ptr, add(48, mod(temp, 10))) temp := div(temp, 10) } temp { temp := div(temp, 10) From 3c49a9894e11b2cc5c26f68b9803b018174b64cf Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 09:26:08 +0000 Subject: [PATCH 04/19] Lint --- src/utils/ECDSA.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index ad4edd72..2f9a5f1f 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -55,9 +55,9 @@ library ECDSA { // The max length of the ASCII reprenstation of a uint256 is 78 bytes. // The length of "\x19Ethereum Signed Message:\n" is 26 bytes. // The next multiple of 32 above 78 + 26 is 128. - + // Instead of allocating, we temporarily copy the 128 bytes before the - // start of `s` data to some variables. + // start of `s` data to some variables. let m3 := mload(sub(s, 0x60)) let m2 := mload(sub(s, 0x40)) let m1 := mload(sub(s, 0x20)) @@ -68,13 +68,13 @@ library ECDSA { // `end` marks the end of the memory which we will compute the keccak256 of. let end := add(ptr, sLength) - + // Convert the length of the bytes to ASCII decimal representation // while copying it over the the memory. for { let temp := sLength ptr := sub(ptr, 1) - mstore8(ptr, add(48, mod(temp, 10))) + mstore8(ptr, add(48, mod(temp, 10))) temp := div(temp, 10) } temp { temp := div(temp, 10) From b6f9b159aecd72a824582969a5134cd5edbce854 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 09:28:16 +0000 Subject: [PATCH 05/19] Fix comments --- src/utils/ECDSA.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 2f9a5f1f..347ff126 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0; /// @notice Gas optimized ECDSA wrapper. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/MerkleProof.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol) +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ECDSA.sol) +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) library ECDSA { function recover(bytes32 hash, bytes calldata signature) internal pure returns (address result) { bytes32 r; From dad8814ef77a7a149751320376bc5418db982423 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 09:51:30 +0000 Subject: [PATCH 06/19] Edit comments --- src/utils/ECDSA.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 347ff126..85a5eb09 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -70,7 +70,7 @@ library ECDSA { let end := add(ptr, sLength) // Convert the length of the bytes to ASCII decimal representation - // while copying it over the the memory. + // and store it into the memory. for { let temp := sLength ptr := sub(ptr, 1) @@ -93,6 +93,7 @@ library ECDSA { // Compute the keccak256 of the memory. result := keccak256(start, sub(end, start)) + // Restore the previous memory. mstore(s, sLength) mstore(sub(s, 0x20), m1) mstore(sub(s, 0x40), m2) From 74a386af8c75097e5a07493734aa7d1d0b364039 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 12:29:49 +0000 Subject: [PATCH 07/19] Add assembly ecrecover staticcall --- .gas-snapshot | 26 ++++++++++++------------- src/test/ECDSA.t.sol | 2 +- src/utils/ECDSA.sol | 45 ++++++++++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 7b331e6a..4ba911d3 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -27,19 +27,19 @@ EDCSATest:testBytes32ToEthSignedMessageHash() (gas: 342) EDCSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) EDCSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) EDCSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1890) -EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1764) -EDCSATest:testRecoverWithInvalidSignature() (gas: 5334) -EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 5138) -EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1914) -EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5233) -EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1913) -EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5160) -EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1912) -EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5213) -EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1890) -EDCSATest:testRecoverWithValidSignature() (gas: 5309) -EDCSATest:testRecoverWithWrongSigner() (gas: 5289) +EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1905) +EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1779) +EDCSATest:testRecoverWithInvalidSignature() (gas: 5208) +EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 5012) +EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1929) +EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5107) +EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1928) +EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5034) +EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1927) +EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5087) +EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1905) +EDCSATest:testRecoverWithValidSignature() (gas: 5183) +EDCSATest:testRecoverWithWrongSigner() (gas: 5163) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/test/ECDSA.t.sol b/src/test/ECDSA.t.sol index 4b33fb0c..1f2c9245 100644 --- a/src/test/ECDSA.t.sol +++ b/src/test/ECDSA.t.sol @@ -120,7 +120,7 @@ contract EDCSATest is DSTestPlus { assertTrue(message.toEthSignedMessageHash() == bytes32(0xa46dbedd405cff161b6e80c17c8567597621d9f4c087204201097cb34448e71b)); } - function recover(bytes32 hash, bytes calldata signature) external pure returns (address) { + function recover(bytes32 hash, bytes calldata signature) external view returns (address) { return ECDSA.recover(hash, signature); } } diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 85a5eb09..3c9cbca6 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -5,15 +5,16 @@ pragma solidity >=0.8.0; /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ECDSA.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) library ECDSA { - function recover(bytes32 hash, bytes calldata signature) internal pure returns (address result) { - bytes32 r; - bytes32 s; - uint8 v; - bool isValid; + function recover(bytes32 hash, bytes calldata signature) internal view returns (address result) { assembly { + // Copy the words above scratch space to some variables temporarily. + let m2 := mload(0x40) + let m3 := mload(0x60) + // Directly load the fields from the calldata. - r := calldataload(signature.offset) - s := calldataload(add(signature.offset, 0x20)) + let s := calldataload(add(signature.offset, 0x20)) + let v + switch signature.length case 65 { v := byte(0, calldataload(add(signature.offset, 0x40))) @@ -24,18 +25,34 @@ library ECDSA { // prettier-ignore s := and(s, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } - // Ensure signature is valid and not malleable. - isValid := and( + + // If signature is valid and not malleable. + if and( // `s` in lower half order. // prettier-ignore lt(s, 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1), // `v` is 27 or 28 byte(v, 0x0101000000) - ) - } - if (isValid) { - // If invalid, the result will be the zero address. - result = ecrecover(hash, v, r, s); + ) { + mstore(0x00, hash) + mstore(0x20, v) + calldatacopy(0x40, signature.offset, 0x20) // Directly copy `r` over. + mstore(0x60, s) + let success := staticcall( + gas(), // Amount of gas left for the transaction. + 0x01, // Address of `ecrecover`. + 0x00, // Start of input. + 0x80, // Size of input. + 0x00, // Start of output. + 0x20 // Size of output + ) + // If invalid, the result will be the zero address. + result := mul(success, and(mload(0x00), 0xffffffffffffffffffffffffffffffffffffffff)) + } + + // Restore the words above scratch space. + mstore(0x40, m2) + mstore(0x60, m3) } } From d3a1058fddcb58c080af9deb1f57f2b4a382e7d8 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 13:09:31 +0000 Subject: [PATCH 08/19] Add comments --- .gas-snapshot | 26 +++++++++++++------------- src/utils/ECDSA.sol | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 4ba911d3..8903470d 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -27,19 +27,19 @@ EDCSATest:testBytes32ToEthSignedMessageHash() (gas: 342) EDCSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) EDCSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) EDCSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1905) -EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1779) -EDCSATest:testRecoverWithInvalidSignature() (gas: 5208) -EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 5012) -EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1929) -EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5107) -EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1928) -EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5034) -EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1927) -EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5087) -EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1905) -EDCSATest:testRecoverWithValidSignature() (gas: 5183) -EDCSATest:testRecoverWithWrongSigner() (gas: 5163) +EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1896) +EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1770) +EDCSATest:testRecoverWithInvalidSignature() (gas: 5199) +EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 5003) +EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1920) +EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5098) +EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1919) +EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5025) +EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1918) +EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5078) +EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1896) +EDCSATest:testRecoverWithValidSignature() (gas: 5174) +EDCSATest:testRecoverWithWrongSigner() (gas: 5154) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 3c9cbca6..93e42c6d 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -8,8 +8,8 @@ library ECDSA { function recover(bytes32 hash, bytes calldata signature) internal view returns (address result) { assembly { // Copy the words above scratch space to some variables temporarily. - let m2 := mload(0x40) let m3 := mload(0x60) + let m2 := mload(0x40) // Directly load the fields from the calldata. let s := calldataload(add(signature.offset, 0x20)) @@ -44,7 +44,7 @@ library ECDSA { 0x00, // Start of input. 0x80, // Size of input. 0x00, // Start of output. - 0x20 // Size of output + 0x20 // Size of output. ) // If invalid, the result will be the zero address. result := mul(success, and(mload(0x00), 0xffffffffffffffffffffffffffffffffffffffff)) From 4086d72c1c99de16dfe70cd6cddbb1f0d107b347 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 21 May 2022 13:24:12 +0000 Subject: [PATCH 09/19] Minor optimization --- .gas-snapshot | 14 +++++++------- src/utils/ECDSA.sol | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 8903470d..d4d699a2 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -29,17 +29,17 @@ EDCSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) EDCSATest:testBytesToEthSignedMessageHashShort() (gas: 629) EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1896) EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1770) -EDCSATest:testRecoverWithInvalidSignature() (gas: 5199) -EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 5003) +EDCSATest:testRecoverWithInvalidSignature() (gas: 5193) +EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4997) EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1920) -EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5098) +EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5092) EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1919) -EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5025) +EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5019) EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1918) -EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5078) +EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5072) EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1896) -EDCSATest:testRecoverWithValidSignature() (gas: 5174) -EDCSATest:testRecoverWithWrongSigner() (gas: 5154) +EDCSATest:testRecoverWithValidSignature() (gas: 5168) +EDCSATest:testRecoverWithWrongSigner() (gas: 5148) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 93e42c6d..93053151 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -47,7 +47,7 @@ library ECDSA { 0x20 // Size of output. ) // If invalid, the result will be the zero address. - result := mul(success, and(mload(0x00), 0xffffffffffffffffffffffffffffffffffffffff)) + result := mul(success, mload(0x00)) } // Restore the words above scratch space. From 7ff3f943453481663ac22797701f28b14b2e43e3 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Wed, 25 May 2022 15:15:11 +0000 Subject: [PATCH 10/19] Minor optimizations --- .gas-snapshot | 26 +++++++++++++------------- src/utils/ECDSA.sol | 10 ++++------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index d4d699a2..0f88a4a1 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -27,19 +27,19 @@ EDCSATest:testBytes32ToEthSignedMessageHash() (gas: 342) EDCSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) EDCSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) EDCSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1896) -EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1770) -EDCSATest:testRecoverWithInvalidSignature() (gas: 5193) -EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4997) -EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1920) -EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5092) -EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1919) -EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5019) -EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1918) -EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5072) -EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1896) -EDCSATest:testRecoverWithValidSignature() (gas: 5168) -EDCSATest:testRecoverWithWrongSigner() (gas: 5148) +EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1873) +EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1747) +EDCSATest:testRecoverWithInvalidSignature() (gas: 5168) +EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4981) +EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1895) +EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5067) +EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1894) +EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5003) +EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1893) +EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5047) +EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1871) +EDCSATest:testRecoverWithValidSignature() (gas: 5143) +EDCSATest:testRecoverWithWrongSigner() (gas: 5123) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 93053151..878f714a 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -13,13 +13,11 @@ library ECDSA { // Directly load the fields from the calldata. let s := calldataload(add(signature.offset, 0x20)) - let v + // Case for `signature.length == 65`, but just do it anyway as it costs less gas. + let v := byte(0, calldataload(add(signature.offset, 0x40))) - switch signature.length - case 65 { - v := byte(0, calldataload(add(signature.offset, 0x40))) - } - case 64 { + // Case for `signature.length == 64`. + if iszero(sub(signature.length, 64)) { // Here, `s` is actually `vs` that needs to be recovered into `v` and `s`. v := add(shr(255, s), 27) // prettier-ignore From f91b3974483aff8af5cc4070584b90291eedffad Mon Sep 17 00:00:00 2001 From: Vectorized Date: Wed, 25 May 2022 15:27:36 +0000 Subject: [PATCH 11/19] Edit comments --- src/utils/ECDSA.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 878f714a..52513d2c 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -13,10 +13,10 @@ library ECDSA { // Directly load the fields from the calldata. let s := calldataload(add(signature.offset, 0x20)) - // Case for `signature.length == 65`, but just do it anyway as it costs less gas. + // If `signature.length == 65`, but just do it anyway as it costs less gas than a switch. let v := byte(0, calldataload(add(signature.offset, 0x40))) - // Case for `signature.length == 64`. + // If `signature.length == 64`. if iszero(sub(signature.length, 64)) { // Here, `s` is actually `vs` that needs to be recovered into `v` and `s`. v := add(shr(255, s), 27) From 818b8c6ab93abc0786791708d7fabc2e6aeb8aab Mon Sep 17 00:00:00 2001 From: Vectorized Date: Wed, 25 May 2022 15:30:43 +0000 Subject: [PATCH 12/19] Fix test class typo --- .gas-snapshot | 34 +++++++++++++++++----------------- src/test/ECDSA.t.sol | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 0f88a4a1..2657271c 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -23,23 +23,23 @@ DSTestPlusTest:testBrutalizeMemory() (gas: 823) DSTestPlusTest:testFailBoundMinBiggerThanMax() (gas: 287) DSTestPlusTest:testMeasuringGas() (gas: 24600) DSTestPlusTest:testRelApproxEqBothZeroesPasses() (gas: 391) -EDCSATest:testBytes32ToEthSignedMessageHash() (gas: 342) -EDCSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) -EDCSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) -EDCSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -EDCSATest:testRecoverWithInvalidLongSignature() (gas: 1873) -EDCSATest:testRecoverWithInvalidShortSignature() (gas: 1747) -EDCSATest:testRecoverWithInvalidSignature() (gas: 5168) -EDCSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4981) -EDCSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1895) -EDCSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5067) -EDCSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1894) -EDCSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5003) -EDCSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1893) -EDCSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5047) -EDCSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1871) -EDCSATest:testRecoverWithValidSignature() (gas: 5143) -EDCSATest:testRecoverWithWrongSigner() (gas: 5123) +ECDSATest:testBytes32ToEthSignedMessageHash() (gas: 342) +ECDSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) +ECDSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) +ECDSATest:testBytesToEthSignedMessageHashShort() (gas: 629) +ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1873) +ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1747) +ECDSATest:testRecoverWithInvalidSignature() (gas: 5168) +ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4981) +ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1895) +ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5067) +ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1894) +ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5003) +ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1893) +ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5047) +ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1871) +ECDSATest:testRecoverWithValidSignature() (gas: 5143) +ECDSATest:testRecoverWithWrongSigner() (gas: 5123) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/test/ECDSA.t.sol b/src/test/ECDSA.t.sol index 1f2c9245..f69c1215 100644 --- a/src/test/ECDSA.t.sol +++ b/src/test/ECDSA.t.sol @@ -5,7 +5,7 @@ import {DSTestPlus} from "./utils/DSTestPlus.sol"; import {ECDSA} from "../utils/ECDSA.sol"; -contract EDCSATest is DSTestPlus { +contract ECDSATest is DSTestPlus { using ECDSA for bytes32; using ECDSA for bytes; From bc5559da38eadf4d646e1840459ee8573ee7d173 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Thu, 26 May 2022 09:00:57 +0000 Subject: [PATCH 13/19] Minor optimization --- .gas-snapshot | 26 +++++++++++++------------- src/utils/ECDSA.sol | 10 ++++------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 2657271c..154c8faf 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -27,19 +27,19 @@ ECDSATest:testBytes32ToEthSignedMessageHash() (gas: 342) ECDSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) ECDSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) ECDSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1873) -ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1747) -ECDSATest:testRecoverWithInvalidSignature() (gas: 5168) -ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4981) -ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1895) -ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5067) -ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1894) -ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5003) -ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1893) -ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5047) -ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1871) -ECDSATest:testRecoverWithValidSignature() (gas: 5143) -ECDSATest:testRecoverWithWrongSigner() (gas: 5123) +ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1870) +ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1744) +ECDSATest:testRecoverWithInvalidSignature() (gas: 5165) +ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4978) +ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1892) +ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5064) +ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1891) +ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5000) +ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1890) +ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5044) +ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1868) +ECDSATest:testRecoverWithValidSignature() (gas: 5140) +ECDSATest:testRecoverWithWrongSigner() (gas: 5120) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 52513d2c..7849ecec 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -7,9 +7,8 @@ pragma solidity >=0.8.0; library ECDSA { function recover(bytes32 hash, bytes calldata signature) internal view returns (address result) { assembly { - // Copy the words above scratch space to some variables temporarily. - let m3 := mload(0x60) - let m2 := mload(0x40) + // Copy the word above scratch space so we can restore it later. + let m := mload(0x40) // Directly load the fields from the calldata. let s := calldataload(add(signature.offset, 0x20)) @@ -48,9 +47,8 @@ library ECDSA { result := mul(success, mload(0x00)) } - // Restore the words above scratch space. - mstore(0x40, m2) - mstore(0x60, m3) + mstore(0x40, m) // Restore the word above scratch space. + mstore(0x60, 0) // Restore the zero slot. } } From e803e3e8c8d42b5b5a2e740e1538e0938e9a0de0 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Tue, 31 May 2022 11:09:14 +0000 Subject: [PATCH 14/19] Add returndatasize check --- src/utils/ECDSA.sol | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 7849ecec..b836880c 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -7,7 +7,7 @@ pragma solidity >=0.8.0; library ECDSA { function recover(bytes32 hash, bytes calldata signature) internal view returns (address result) { assembly { - // Copy the word above scratch space so we can restore it later. + // Copy the free memory pointer so that we can restore it later. let m := mload(0x40) // Directly load the fields from the calldata. @@ -40,15 +40,16 @@ library ECDSA { 0x01, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. - 0x00, // Start of output. + 0x40, // Start of output. 0x20 // Size of output. ) - // If invalid, the result will be the zero address. - result := mul(success, mload(0x00)) + // Restore the zero slot. + mstore(0x60, 0) + // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. + result := mload(sub(0x60, mul(returndatasize(), success))) } - - mstore(0x40, m) // Restore the word above scratch space. - mstore(0x60, 0) // Restore the zero slot. + // Restore the free memory pointer. + mstore(0x40, m) } } From 35e9ce8c35ec700b8233009e856cfd7c08016b5a Mon Sep 17 00:00:00 2001 From: Vectorized Date: Tue, 31 May 2022 11:11:36 +0000 Subject: [PATCH 15/19] Lint and snapshot --- .gas-snapshot | 26 +++++++++++++------------- src/utils/ECDSA.sol | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 154c8faf..97953e23 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -27,19 +27,19 @@ ECDSATest:testBytes32ToEthSignedMessageHash() (gas: 342) ECDSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) ECDSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) ECDSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1870) -ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1744) -ECDSATest:testRecoverWithInvalidSignature() (gas: 5165) -ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4978) -ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1892) -ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5064) -ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1891) -ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5000) -ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1890) -ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5044) -ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1868) -ECDSATest:testRecoverWithValidSignature() (gas: 5140) -ECDSATest:testRecoverWithWrongSigner() (gas: 5120) +ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1861) +ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1735) +ECDSATest:testRecoverWithInvalidSignature() (gas: 5175) +ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4988) +ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1883) +ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5074) +ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1882) +ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5010) +ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1881) +ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5054) +ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1859) +ECDSATest:testRecoverWithValidSignature() (gas: 5150) +ECDSATest:testRecoverWithWrongSigner() (gas: 5130) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index b836880c..47f05bef 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -44,7 +44,7 @@ library ECDSA { 0x20 // Size of output. ) // Restore the zero slot. - mstore(0x60, 0) + mstore(0x60, 0) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(sub(0x60, mul(returndatasize(), success))) } From d825326cda198e168695250d31163b0dff07a6ec Mon Sep 17 00:00:00 2001 From: Vectorized Date: Fri, 3 Jun 2022 19:19:05 +0000 Subject: [PATCH 16/19] Change to MIT license --- .gas-snapshot | 14 +++++++------- src/test/ECDSA.t.sol | 2 +- src/utils/ECDSA.sol | 20 +++++++++++--------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 97953e23..c1c9bc73 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -29,17 +29,17 @@ ECDSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) ECDSATest:testBytesToEthSignedMessageHashShort() (gas: 629) ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1861) ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1735) -ECDSATest:testRecoverWithInvalidSignature() (gas: 5175) -ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4988) +ECDSATest:testRecoverWithInvalidSignature() (gas: 5167) +ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4980) ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1883) -ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5074) +ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5066) ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1882) -ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5010) +ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5002) ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1881) -ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5054) +ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5046) ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1859) -ECDSATest:testRecoverWithValidSignature() (gas: 5150) -ECDSATest:testRecoverWithWrongSigner() (gas: 5130) +ECDSATest:testRecoverWithValidSignature() (gas: 5142) +ECDSATest:testRecoverWithWrongSigner() (gas: 5122) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/test/ECDSA.t.sol b/src/test/ECDSA.t.sol index f69c1215..6c0c2f28 100644 --- a/src/test/ECDSA.t.sol +++ b/src/test/ECDSA.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: AGPL-3.0-only +// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import {DSTestPlus} from "./utils/DSTestPlus.sol"; diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 47f05bef..c3d31ab1 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: AGPL-3.0-only +// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; /// @notice Gas optimized ECDSA wrapper. @@ -35,18 +35,20 @@ library ECDSA { mstore(0x20, v) calldatacopy(0x40, signature.offset, 0x20) // Directly copy `r` over. mstore(0x60, s) - let success := staticcall( - gas(), // Amount of gas left for the transaction. - 0x01, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x40, // Start of output. - 0x20 // Size of output. + pop( + staticcall( + gas(), // Amount of gas left for the transaction. + 0x01, // Address of `ecrecover`. + 0x00, // Start of input. + 0x80, // Size of input. + 0x40, // Start of output. + 0x20 // Size of output. + ) ) // Restore the zero slot. mstore(0x60, 0) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - result := mload(sub(0x60, mul(returndatasize(), success))) + result := mload(sub(0x60, returndatasize())) } // Restore the free memory pointer. mstore(0x40, m) From 07c4cdd781a8f7ec94439229625a201240567b36 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sat, 4 Jun 2022 15:24:08 +0000 Subject: [PATCH 17/19] Use back regular switch --- .gas-snapshot | 26 +++++++++++++------------- src/utils/ECDSA.sol | 17 ++++++++++------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index c1c9bc73..6a1b65e5 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -27,19 +27,19 @@ ECDSATest:testBytes32ToEthSignedMessageHash() (gas: 342) ECDSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) ECDSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) ECDSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1861) -ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1735) -ECDSATest:testRecoverWithInvalidSignature() (gas: 5167) -ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4980) -ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1883) -ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5066) -ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1882) -ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5002) -ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1881) -ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5046) -ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1859) -ECDSATest:testRecoverWithValidSignature() (gas: 5142) -ECDSATest:testRecoverWithWrongSigner() (gas: 5122) +ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1884) +ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1758) +ECDSATest:testRecoverWithInvalidSignature() (gas: 5203) +ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4985) +ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1919) +ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5102) +ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1918) +ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5007) +ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1917) +ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5082) +ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1895) +ECDSATest:testRecoverWithValidSignature() (gas: 5178) +ECDSATest:testRecoverWithWrongSigner() (gas: 5158) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index c3d31ab1..65a47906 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -9,26 +9,29 @@ library ECDSA { assembly { // Copy the free memory pointer so that we can restore it later. let m := mload(0x40) - - // Directly load the fields from the calldata. + // Directly load `s` from the calldata. let s := calldataload(add(signature.offset, 0x20)) - // If `signature.length == 65`, but just do it anyway as it costs less gas than a switch. - let v := byte(0, calldataload(add(signature.offset, 0x40))) - // If `signature.length == 64`. - if iszero(sub(signature.length, 64)) { + let v := 0 + + switch signature.length + case 64 { // Here, `s` is actually `vs` that needs to be recovered into `v` and `s`. v := add(shr(255, s), 27) // prettier-ignore s := and(s, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } + case 65 { + // Directly load `v` from the calldata. + v := byte(0, calldataload(add(signature.offset, 0x40))) + } // If signature is valid and not malleable. if and( // `s` in lower half order. // prettier-ignore lt(s, 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1), - // `v` is 27 or 28 + // `v` is 27 or 28. byte(v, 0x0101000000) ) { mstore(0x00, hash) From e8aaa05958625d05a8180683c1f02f9a6e319b8c Mon Sep 17 00:00:00 2001 From: Vectorized Date: Wed, 8 Jun 2022 10:08:48 +0000 Subject: [PATCH 18/19] Remove 27 28 check, as recommended by axic --- .gas-snapshot | 26 +++++++++++++------------- src/utils/ECDSA.sol | 11 +++-------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 6a1b65e5..d4020e73 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -27,19 +27,19 @@ ECDSATest:testBytes32ToEthSignedMessageHash() (gas: 342) ECDSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) ECDSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) ECDSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -ECDSATest:testRecoverWithInvalidLongSignature() (gas: 1884) -ECDSATest:testRecoverWithInvalidShortSignature() (gas: 1758) -ECDSATest:testRecoverWithInvalidSignature() (gas: 5203) -ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4985) -ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 1919) -ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5102) -ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 1918) -ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 5007) -ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 1917) -ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5082) -ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 1895) -ECDSATest:testRecoverWithValidSignature() (gas: 5178) -ECDSATest:testRecoverWithWrongSigner() (gas: 5158) +ECDSATest:testRecoverWithInvalidLongSignature() (gas: 5055) +ECDSATest:testRecoverWithInvalidShortSignature() (gas: 4929) +ECDSATest:testRecoverWithInvalidSignature() (gas: 5188) +ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4970) +ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 5090) +ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5087) +ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 5089) +ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 4992) +ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 5088) +ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5067) +ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 5066) +ECDSATest:testRecoverWithValidSignature() (gas: 5163) +ECDSATest:testRecoverWithWrongSigner() (gas: 5143) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index 65a47906..f68eabb0 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -26,14 +26,9 @@ library ECDSA { v := byte(0, calldataload(add(signature.offset, 0x40))) } - // If signature is valid and not malleable. - if and( - // `s` in lower half order. - // prettier-ignore - lt(s, 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1), - // `v` is 27 or 28. - byte(v, 0x0101000000) - ) { + // `s` in lower half order. + // prettier-ignore + if iszero(gt(s, 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0)) { mstore(0x00, hash) mstore(0x20, v) calldatacopy(0x40, signature.offset, 0x20) // Directly copy `r` over. From b618b264e0bfe09102e942381d45734976515f38 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Wed, 8 Jun 2022 10:55:52 +0000 Subject: [PATCH 19/19] Minor optimizations --- .gas-snapshot | 26 +++++++++++++------------- src/utils/ECDSA.sol | 12 +++++------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index d4020e73..d92d06b9 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -27,19 +27,19 @@ ECDSATest:testBytes32ToEthSignedMessageHash() (gas: 342) ECDSATest:testBytesToEthSignedMessageHashEmpty() (gas: 610) ECDSATest:testBytesToEthSignedMessageHashEmptyLong() (gas: 771) ECDSATest:testBytesToEthSignedMessageHashShort() (gas: 629) -ECDSATest:testRecoverWithInvalidLongSignature() (gas: 5055) -ECDSATest:testRecoverWithInvalidShortSignature() (gas: 4929) -ECDSATest:testRecoverWithInvalidSignature() (gas: 5188) -ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4970) -ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 5090) -ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5087) -ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 5089) -ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 4992) -ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 5088) -ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5067) -ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 5066) -ECDSATest:testRecoverWithValidSignature() (gas: 5163) -ECDSATest:testRecoverWithWrongSigner() (gas: 5143) +ECDSATest:testRecoverWithInvalidLongSignature() (gas: 5041) +ECDSATest:testRecoverWithInvalidShortSignature() (gas: 4915) +ECDSATest:testRecoverWithInvalidSignature() (gas: 5175) +ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4962) +ECDSATest:testRecoverWithV0SignatureWithVersion00() (gas: 5077) +ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 5074) +ECDSATest:testRecoverWithV0SignatureWithWrongVersion() (gas: 5076) +ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 4984) +ECDSATest:testRecoverWithV1SignatureWithVersion01() (gas: 5075) +ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 5054) +ECDSATest:testRecoverWithV1SignatureWithWrongVersion() (gas: 5053) +ECDSATest:testRecoverWithValidSignature() (gas: 5150) +ECDSATest:testRecoverWithWrongSigner() (gas: 5130) ERC1155Test:testApproveAll() (gas: 31053) ERC1155Test:testBatchBalanceOf() (gas: 157552) ERC1155Test:testBatchBurn() (gas: 151044) diff --git a/src/utils/ECDSA.sol b/src/utils/ECDSA.sol index f68eabb0..03b6ce0d 100644 --- a/src/utils/ECDSA.sol +++ b/src/utils/ECDSA.sol @@ -12,25 +12,23 @@ library ECDSA { // Directly load `s` from the calldata. let s := calldataload(add(signature.offset, 0x20)) - let v := 0 - switch signature.length case 64 { // Here, `s` is actually `vs` that needs to be recovered into `v` and `s`. - v := add(shr(255, s), 27) + // Compute `v` and store it in the scratch space. + mstore(0x20, add(shr(255, s), 27)) // prettier-ignore s := and(s, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } case 65 { - // Directly load `v` from the calldata. - v := byte(0, calldataload(add(signature.offset, 0x40))) + // Compute `v` and store it in the scratch space. + mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) } - // `s` in lower half order. + // If `s` in lower half order, such that the signature is not malleable. // prettier-ignore if iszero(gt(s, 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0)) { mstore(0x00, hash) - mstore(0x20, v) calldatacopy(0x40, signature.offset, 0x20) // Directly copy `r` over. mstore(0x60, s) pop(