Created
May 4, 2019 03:19
-
-
Save cwhinfrey/925bf43411c2af8cc024aef649c7a82b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
contract ContractSignerVerifier { | |
// returns keccak256("isValidSigner") if signer is valid | |
function isValidSigner(address _signer, bytes memory data) public view returns (bytes32); | |
} | |
interface IChannelArbiter { | |
function channelValueForUpdate(address sender, address receiver, uint256 nonce) external view returns (uint256 value); | |
} | |
contract ChannelIdentifier { | |
function getChannelId(address sender, address receiver, IChannelArbiter arbiter) public pure returns (bytes32) { | |
return keccak256(abi.encodePacked(sender, receiver, address(arbiter))); | |
} | |
} | |
library Address { | |
/** | |
* Returns whether the target address is a contract | |
* @dev This function will return false if invoked during the constructor of a contract, | |
* as the code is not actually created until after the constructor finishes. | |
* @param account address of the account to check | |
* @return whether the target address is a contract | |
*/ | |
function isContract(address account) internal view returns (bool) { | |
uint256 size; | |
// XXX Currently there is no better way to check if there is a contract in an address | |
// than to check the size of the code at that address. | |
// See https://ethereum.stackexchange.com/a/14016/36603 | |
// for more details about how this works. | |
// TODO Check this again before the Serenity release, because all addresses will be | |
// contracts then. | |
// solhint-disable-next-line no-inline-assembly | |
assembly { size := extcodesize(account) } | |
return size > 0; | |
} | |
} | |
library ECDSA { | |
/** | |
* @dev Recover signer address from a message by using their signature. | |
* @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address. | |
* @param signature bytes signature, the signature is generated using web3.eth.sign() | |
*/ | |
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { | |
// Check the signature length | |
if (signature.length != 65) { | |
return (address(0)); | |
} | |
// 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))) | |
} | |
// 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. | |
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { | |
return address(0); | |
} | |
if (v != 27 && v != 28) { | |
return address(0); | |
} | |
// If the signature is valid (and not malleable), return the signer address | |
return ecrecover(hash, v, r, s); | |
} | |
/** | |
* toEthSignedMessageHash | |
* @dev Prefix a bytes32 value with "\x19Ethereum Signed Message:" | |
* and hash the result. | |
*/ | |
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)); | |
} | |
} | |
contract PaymentChannelArbiter is IChannelArbiter, ChannelIdentifier { | |
using ECDSA for bytes32; | |
using Address for address; | |
// mapping of the channelId to a mapping of the nonce to the channel value | |
mapping (bytes32 => mapping(uint256 => uint256)) private updates; | |
function updateChannel( | |
address sender, | |
address receiver, | |
uint256 nonce, | |
uint256 channelValue, | |
bytes memory signature | |
) | |
public | |
{ | |
bytes32 messageHash = keccak256(abi.encodePacked(sender, receiver, address(this), nonce, channelValue)); | |
address signer = messageHash.toEthSignedMessageHash().recover(signature); | |
if (sender.isContract()) { | |
require(ContractSignerVerifier(sender).isValidSigner(signer, "") == keccak256("isValidSigner")); | |
} else { | |
require(sender == signer); | |
} | |
bytes32 channelId = getChannelId(sender, receiver, this); | |
updates[channelId][nonce] = channelValue; | |
} | |
function channelValueForUpdate(address sender, address receiver, uint256 nonce) external view returns (uint256 channelValue) { | |
bytes32 channelId = getChannelId(sender, receiver, this); | |
return updates[channelId][nonce]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment