From 0bcff19da444699239b91e2914b44f2edd89028e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Wed, 14 Jun 2023 16:12:59 +0200 Subject: [PATCH] prettier things --- .prettierrc | 3 + examples/BlindAuction.sol | 213 ++++++++++++----------- examples/CMUX.sol | 43 +++-- examples/EIP712.sol | 16 +- examples/EncryptedERC20.sol | 213 +++++++++++++---------- examples/OptimisticRequire.sol | 112 ++++++------ examples/SmallEncryptedERC20.sol | 209 ++++++++++++---------- examples/abstract/EIP712WithModifier.sol | 28 ++- lib/Impl.sol | 133 ++++++++++++-- lib/TFHE.sol | 68 ++++++-- 10 files changed, 639 insertions(+), 399 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..1ca87ab --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": false +} diff --git a/examples/BlindAuction.sol b/examples/BlindAuction.sol index 82ad0a1..cbfff30 100644 --- a/examples/BlindAuction.sol +++ b/examples/BlindAuction.sol @@ -2,122 +2,137 @@ pragma solidity >=0.8.13 <0.9.0; -import '../lib/TFHE.sol'; +import "../lib/TFHE.sol"; -import './abstract/EIP712WithModifier.sol'; +import "./abstract/EIP712WithModifier.sol"; -import './SmallEncryptedERC20.sol'; +import "./SmallEncryptedERC20.sol"; contract BlindAuction is EIP712WithModifier { - uint public endTime; + uint public endTime; - address public beneficiary; + address public beneficiary; - // Current highest bid. - euint8 internal highestBid; + // Current highest bid. + euint8 internal highestBid; - // Mapping from bidder to their bid value. - mapping(address => euint8) public bids; + // Mapping from bidder to their bid value. + mapping(address => euint8) public bids; - // The token contract used for encrypted bids. - SmallEncryptedERC20 public tokenContract; + // The token contract used for encrypted bids. + SmallEncryptedERC20 public tokenContract; - // Whether the auction object has been claimed. - bool public objectClaimed; + // Whether the auction object has been claimed. + bool public objectClaimed; - // If the token has been transferred to the beneficiary - bool public tokenTransferred; + // If the token has been transferred to the beneficiary + bool public tokenTransferred; - // The function has been called too early. - // Try again at `time`. - error TooEarly(uint time); - // The function has been called too late. - // It cannot be called after `time`. - error TooLate(uint time); + // The function has been called too early. + // Try again at `time`. + error TooEarly(uint time); + // The function has been called too late. + // It cannot be called after `time`. + error TooLate(uint time); - event Winner(address who); + event Winner(address who); - constructor( - address _beneficiary, - SmallEncryptedERC20 _tokenContract, - uint biddingTime - ) EIP712WithModifier('Authorization token', '1') { - beneficiary = _beneficiary; - tokenContract = _tokenContract; - endTime = block.timestamp + biddingTime; - objectClaimed = false; - tokenTransferred = false; - } - - // Bid an `encryptedValue`. - function bid(bytes calldata encryptedValue) public onlyBeforeEnd { - euint8 value = TFHE.asEuint8(encryptedValue); - euint8 existingBid = bids[msg.sender]; - if (euint8.unwrap(existingBid) != 0) { - euint8 isHigher = TFHE.lt(existingBid, value); - // Update bid with value - bids[msg.sender] = TFHE.cmux(isHigher, value, existingBid); - // Transfer only the difference between existing and value - euint8 toTransfer = TFHE.sub(value, existingBid); - // Transfer only if bid is higher - tokenContract.transferFrom(msg.sender, address(this), TFHE.mul(isHigher, toTransfer)); - } else { - bids[msg.sender] = value; - tokenContract.transferFrom(msg.sender, address(this), value); + constructor( + address _beneficiary, + SmallEncryptedERC20 _tokenContract, + uint biddingTime + ) EIP712WithModifier("Authorization token", "1") { + beneficiary = _beneficiary; + tokenContract = _tokenContract; + endTime = block.timestamp + biddingTime; + objectClaimed = false; + tokenTransferred = false; } - euint8 currentBid = bids[msg.sender]; - if (euint8.unwrap(highestBid) == 0) { - highestBid = currentBid; - } else { - highestBid = TFHE.cmux(TFHE.lt(highestBid, currentBid), currentBid, highestBid); + + // Bid an `encryptedValue`. + function bid(bytes calldata encryptedValue) public onlyBeforeEnd { + euint8 value = TFHE.asEuint8(encryptedValue); + euint8 existingBid = bids[msg.sender]; + if (euint8.unwrap(existingBid) != 0) { + euint8 isHigher = TFHE.lt(existingBid, value); + // Update bid with value + bids[msg.sender] = TFHE.cmux(isHigher, value, existingBid); + // Transfer only the difference between existing and value + euint8 toTransfer = TFHE.sub(value, existingBid); + // Transfer only if bid is higher + tokenContract.transferFrom( + msg.sender, + address(this), + TFHE.mul(isHigher, toTransfer) + ); + } else { + bids[msg.sender] = value; + tokenContract.transferFrom(msg.sender, address(this), value); + } + euint8 currentBid = bids[msg.sender]; + if (euint8.unwrap(highestBid) == 0) { + highestBid = currentBid; + } else { + highestBid = TFHE.cmux( + TFHE.lt(highestBid, currentBid), + currentBid, + highestBid + ); + } } - } - // Returns an encrypted value of 0 or 1 under the caller's public key, indicating - // if the caller has the highest bid. - function doIHaveHighestBid( - bytes32 publicKey, - bytes calldata signature - ) public view onlyAfterEnd onlySignedPublicKey(publicKey, signature) returns (bytes memory) { - return TFHE.reencrypt(TFHE.lte(highestBid, bids[msg.sender]), publicKey); - } - - // Claim the object. Succeeds only if the caller has the highest bid. - function claim() public onlyAfterEnd { - require(!objectClaimed); - TFHE.requireCt(TFHE.lte(highestBid, bids[msg.sender])); - - objectClaimed = true; - bids[msg.sender] = euint8.wrap(0); - emit Winner(msg.sender); - } - - // Transfer token to beneficiary - function auctionEnd() public onlyAfterEnd { - require(!tokenTransferred); - - tokenTransferred = true; - - tokenContract.transfer(beneficiary, highestBid); - } - - // Withdraw a bid from the auction to the caller once the auction has stopped. - function withdraw() public onlyAfterEnd { - euint8 bidValue = bids[msg.sender]; - if (!objectClaimed) { - TFHE.requireCt(TFHE.lt(bidValue, highestBid)); + // Returns an encrypted value of 0 or 1 under the caller's public key, indicating + // if the caller has the highest bid. + function doIHaveHighestBid( + bytes32 publicKey, + bytes calldata signature + ) + public + view + onlyAfterEnd + onlySignedPublicKey(publicKey, signature) + returns (bytes memory) + { + return + TFHE.reencrypt(TFHE.lte(highestBid, bids[msg.sender]), publicKey); } - tokenContract.transfer(msg.sender, bidValue); - bids[msg.sender] = euint8.wrap(0); - } - modifier onlyBeforeEnd() { - if (block.timestamp >= endTime) revert TooLate(endTime); - _; - } + // Claim the object. Succeeds only if the caller has the highest bid. + function claim() public onlyAfterEnd { + require(!objectClaimed); + TFHE.requireCt(TFHE.lte(highestBid, bids[msg.sender])); - modifier onlyAfterEnd() { - if (block.timestamp <= endTime) revert TooEarly(endTime); - _; - } + objectClaimed = true; + bids[msg.sender] = euint8.wrap(0); + emit Winner(msg.sender); + } + + // Transfer token to beneficiary + function auctionEnd() public onlyAfterEnd { + require(!tokenTransferred); + + tokenTransferred = true; + + tokenContract.transfer(beneficiary, highestBid); + } + + // Withdraw a bid from the auction to the caller once the auction has stopped. + function withdraw() public onlyAfterEnd { + euint8 bidValue = bids[msg.sender]; + if (!objectClaimed) { + TFHE.requireCt(TFHE.lt(bidValue, highestBid)); + } + tokenContract.transfer(msg.sender, bidValue); + bids[msg.sender] = euint8.wrap(0); + } + + modifier onlyBeforeEnd() { + if (block.timestamp >= endTime) revert TooLate(endTime); + _; + } + + modifier onlyAfterEnd() { + if (block.timestamp <= endTime) revert TooEarly(endTime); + _; + } } diff --git a/examples/CMUX.sol b/examples/CMUX.sol index be518ad..120a04f 100644 --- a/examples/CMUX.sol +++ b/examples/CMUX.sol @@ -2,27 +2,36 @@ pragma solidity >=0.8.13 <0.9.0; -import './abstract/EIP712WithModifier.sol'; -import '../lib/TFHE.sol'; +import "./abstract/EIP712WithModifier.sol"; +import "../lib/TFHE.sol"; // Shows the CMUX operation in Solidity. contract CMUX is EIP712WithModifier { - euint8 internal result; + euint8 internal result; - constructor() EIP712WithModifier('Authorization token', '1') {} + constructor() EIP712WithModifier("Authorization token", "1") {} - // Set result = if control { ifTrue } else { ifFalse } - function cmux(bytes calldata controlBytes, bytes calldata ifTrueBytes, bytes calldata ifFalseBytes) public { - euint8 control = TFHE.asEuint8(controlBytes); - euint8 ifTrue = TFHE.asEuint8(ifTrueBytes); - euint8 ifFalse = TFHE.asEuint8(ifFalseBytes); - result = TFHE.cmux(control, ifTrue, ifFalse); - } + // Set result = if control { ifTrue } else { ifFalse } + function cmux( + bytes calldata controlBytes, + bytes calldata ifTrueBytes, + bytes calldata ifFalseBytes + ) public { + euint8 control = TFHE.asEuint8(controlBytes); + euint8 ifTrue = TFHE.asEuint8(ifTrueBytes); + euint8 ifFalse = TFHE.asEuint8(ifFalseBytes); + result = TFHE.cmux(control, ifTrue, ifFalse); + } - function getResult( - bytes32 publicKey, - bytes calldata signature - ) public view onlySignedPublicKey(publicKey, signature) returns (bytes memory) { - return TFHE.reencrypt(result, publicKey); - } + function getResult( + bytes32 publicKey, + bytes calldata signature + ) + public + view + onlySignedPublicKey(publicKey, signature) + returns (bytes memory) + { + return TFHE.reencrypt(result, publicKey); + } } diff --git a/examples/EIP712.sol b/examples/EIP712.sol index db88351..4e34dcc 100644 --- a/examples/EIP712.sol +++ b/examples/EIP712.sol @@ -2,15 +2,15 @@ pragma solidity >=0.8.13 <0.9.0; -import './abstract/EIP712WithModifier.sol'; +import "./abstract/EIP712WithModifier.sol"; contract AuthorizationToken is EIP712WithModifier { - constructor() EIP712WithModifier('Authorization token', '1') {} + constructor() EIP712WithModifier("Authorization token", "1") {} - function verify( - bytes32 publicKey, - bytes calldata signature - ) public view onlySignedPublicKey(publicKey, signature) returns (bool) { - return true; - } + function verify( + bytes32 publicKey, + bytes calldata signature + ) public view onlySignedPublicKey(publicKey, signature) returns (bool) { + return true; + } } diff --git a/examples/EncryptedERC20.sol b/examples/EncryptedERC20.sol index 89e737f..b353e4d 100644 --- a/examples/EncryptedERC20.sol +++ b/examples/EncryptedERC20.sol @@ -2,121 +2,148 @@ pragma solidity >=0.8.13 <0.9.0; -import './abstract/EIP712WithModifier.sol'; +import "./abstract/EIP712WithModifier.sol"; -import '../lib/TFHE.sol'; +import "../lib/TFHE.sol"; contract EncryptedERC20 is EIP712WithModifier { - euint32 private totalSupply; - string public constant name = 'Naraggara'; // City of Zama's battle - string public constant symbol = 'NARA'; - uint8 public constant decimals = 18; + euint32 private totalSupply; + string public constant name = "Naraggara"; // City of Zama's battle + string public constant symbol = "NARA"; + uint8 public constant decimals = 18; - // used for output authorization - bytes32 private DOMAIN_SEPARATOR; + // used for output authorization + bytes32 private DOMAIN_SEPARATOR; - // A mapping from address to an encrypted balance. - mapping(address => euint32) internal balances; + // A mapping from address to an encrypted balance. + mapping(address => euint32) internal balances; - // A mapping of the form mapping(owner => mapping(spender => allowance)). - mapping(address => mapping(address => euint32)) internal allowances; + // A mapping of the form mapping(owner => mapping(spender => allowance)). + mapping(address => mapping(address => euint32)) internal allowances; - // The owner of the contract. - address internal contractOwner; + // The owner of the contract. + address internal contractOwner; - constructor() EIP712WithModifier('Authorization token', '1') { - contractOwner = msg.sender; - } + constructor() EIP712WithModifier("Authorization token", "1") { + contractOwner = msg.sender; + } - // Sets the balance of the owner to the given encrypted balance. - function mint(bytes calldata encryptedAmount) public onlyContractOwner { - euint32 amount = TFHE.asEuint32(encryptedAmount); - balances[contractOwner] = amount; - totalSupply = TFHE.add(totalSupply, amount); - } + // Sets the balance of the owner to the given encrypted balance. + function mint(bytes calldata encryptedAmount) public onlyContractOwner { + euint32 amount = TFHE.asEuint32(encryptedAmount); + balances[contractOwner] = amount; + totalSupply = TFHE.add(totalSupply, amount); + } - // Transfers an encrypted amount from the message sender address to the `to` address. - function transfer(address to, bytes calldata encryptedAmount) public { - transfer(to, TFHE.asEuint32(encryptedAmount)); - } + // Transfers an encrypted amount from the message sender address to the `to` address. + function transfer(address to, bytes calldata encryptedAmount) public { + transfer(to, TFHE.asEuint32(encryptedAmount)); + } - // Transfers an amount from the message sender address to the `to` address. - function transfer(address to, euint32 amount) internal { - _transfer(msg.sender, to, amount); - } + // Transfers an amount from the message sender address to the `to` address. + function transfer(address to, euint32 amount) internal { + _transfer(msg.sender, to, amount); + } - function getTotalSupply( - bytes32 publicKey, - bytes calldata signature - ) public view onlyContractOwner onlySignedPublicKey(publicKey, signature) returns (bytes memory) { - return TFHE.reencrypt(totalSupply, publicKey); - } + function getTotalSupply( + bytes32 publicKey, + bytes calldata signature + ) + public + view + onlyContractOwner + onlySignedPublicKey(publicKey, signature) + returns (bytes memory) + { + return TFHE.reencrypt(totalSupply, publicKey); + } - // Returns the balance of the caller under their public FHE key. - // The FHE public key is automatically determined based on the origin of the call. - function balanceOf( - bytes32 publicKey, - bytes calldata signature - ) public view onlySignedPublicKey(publicKey, signature) returns (bytes memory) { - return TFHE.reencrypt(balances[msg.sender], publicKey); - } + // Returns the balance of the caller under their public FHE key. + // The FHE public key is automatically determined based on the origin of the call. + function balanceOf( + bytes32 publicKey, + bytes calldata signature + ) + public + view + onlySignedPublicKey(publicKey, signature) + returns (bytes memory) + { + return TFHE.reencrypt(balances[msg.sender], publicKey); + } - // Sets the `encryptedAmount` as the allowance of `spender` over the caller's tokens. - function approve(address spender, bytes calldata encryptedAmount) public { - address owner = msg.sender; - _approve(owner, spender, TFHE.asEuint32(encryptedAmount)); - } + // Sets the `encryptedAmount` as the allowance of `spender` over the caller's tokens. + function approve(address spender, bytes calldata encryptedAmount) public { + address owner = msg.sender; + _approve(owner, spender, TFHE.asEuint32(encryptedAmount)); + } - // Returns the remaining number of tokens that `spender` is allowed to spend - // on behalf of the caller. The returned ciphertext is under the caller public FHE key. - function allowance( - address spender, - bytes32 publicKey, - bytes calldata signature - ) public view onlySignedPublicKey(publicKey, signature) returns (bytes memory) { - address owner = msg.sender; + // Returns the remaining number of tokens that `spender` is allowed to spend + // on behalf of the caller. The returned ciphertext is under the caller public FHE key. + function allowance( + address spender, + bytes32 publicKey, + bytes calldata signature + ) + public + view + onlySignedPublicKey(publicKey, signature) + returns (bytes memory) + { + address owner = msg.sender; - return TFHE.reencrypt(_allowance(owner, spender), publicKey); - } + return TFHE.reencrypt(_allowance(owner, spender), publicKey); + } - // Transfers `encryptedAmount` tokens using the caller's allowance. - function transferFrom(address from, address to, bytes calldata encryptedAmount) public { - transferFrom(from, to, TFHE.asEuint32(encryptedAmount)); - } + // Transfers `encryptedAmount` tokens using the caller's allowance. + function transferFrom( + address from, + address to, + bytes calldata encryptedAmount + ) public { + transferFrom(from, to, TFHE.asEuint32(encryptedAmount)); + } - // Transfers `amount` tokens using the caller's allowance. - function transferFrom(address from, address to, euint32 amount) public { - address spender = msg.sender; - _updateAllowance(from, spender, amount); - _transfer(from, to, amount); - } + // Transfers `amount` tokens using the caller's allowance. + function transferFrom(address from, address to, euint32 amount) public { + address spender = msg.sender; + _updateAllowance(from, spender, amount); + _transfer(from, to, amount); + } - function _approve(address owner, address spender, euint32 amount) internal { - allowances[owner][spender] = amount; - } + function _approve(address owner, address spender, euint32 amount) internal { + allowances[owner][spender] = amount; + } - function _allowance(address owner, address spender) internal view returns (euint32) { - return allowances[owner][spender]; - } + function _allowance( + address owner, + address spender + ) internal view returns (euint32) { + return allowances[owner][spender]; + } - function _updateAllowance(address owner, address spender, euint32 amount) internal { - euint32 currentAllowance = _allowance(owner, spender); - TFHE.requireCt(TFHE.lte(amount, currentAllowance)); - _approve(owner, spender, TFHE.sub(currentAllowance, amount)); - } + function _updateAllowance( + address owner, + address spender, + euint32 amount + ) internal { + euint32 currentAllowance = _allowance(owner, spender); + TFHE.requireCt(TFHE.lte(amount, currentAllowance)); + _approve(owner, spender, TFHE.sub(currentAllowance, amount)); + } - // Transfers an encrypted amount. - function _transfer(address from, address to, euint32 amount) internal { - // Make sure the sender has enough tokens. - TFHE.requireCt(TFHE.lte(amount, balances[from])); + // Transfers an encrypted amount. + function _transfer(address from, address to, euint32 amount) internal { + // Make sure the sender has enough tokens. + TFHE.requireCt(TFHE.lte(amount, balances[from])); - // Add to the balance of `to` and subract from the balance of `from`. - balances[to] = TFHE.add(balances[to], amount); - balances[from] = TFHE.sub(balances[from], amount); - } + // Add to the balance of `to` and subract from the balance of `from`. + balances[to] = TFHE.add(balances[to], amount); + balances[from] = TFHE.sub(balances[from], amount); + } - modifier onlyContractOwner() { - require(msg.sender == contractOwner); - _; - } + modifier onlyContractOwner() { + require(msg.sender == contractOwner); + _; + } } diff --git a/examples/OptimisticRequire.sol b/examples/OptimisticRequire.sol index 8250f26..9346bd0 100644 --- a/examples/OptimisticRequire.sol +++ b/examples/OptimisticRequire.sol @@ -7,75 +7,75 @@ import "../lib/TFHE.sol"; // Example use of optimistic ciphertext requires. Aims to show the different gas usage // as compared to syncrhonous non-optimistic and plaintext ones. contract OptimisticRequire { - euint32 internal ct1; - euint32 internal ct2; - uint256 internal value; - uint256 internal iterations; + euint32 internal ct1; + euint32 internal ct2; + uint256 internal value; + uint256 internal iterations; - constructor() {} + constructor() {} - function init(bytes calldata ctBytes, uint256 _iterations) public { - ct1 = TFHE.asEuint32(ctBytes); - ct2 = TFHE.asEuint32(ctBytes); - iterations = _iterations; - value = 1; - } - - function doWorkToPayGas() internal { - uint256 newValue = value; - for (uint256 i = 0; i < iterations; i++) { - newValue += 2; + function init(bytes calldata ctBytes, uint256 _iterations) public { + ct1 = TFHE.asEuint32(ctBytes); + ct2 = TFHE.asEuint32(ctBytes); + iterations = _iterations; + value = 1; } - value = newValue; - } - function getValue() public view returns (uint256) { - return value; - } + function doWorkToPayGas() internal { + uint256 newValue = value; + for (uint256 i = 0; i < iterations; i++) { + newValue += 2; + } + value = newValue; + } - // Charge full gas as both requires are true. - function optimisticRequireCtTrue() public { - // True. - TFHE.optimisticRequireCt(TFHE.lte(ct1, ct2)); + function getValue() public view returns (uint256) { + return value; + } - // True. - TFHE.optimisticRequireCt(TFHE.lte(ct1, ct2)); + // Charge full gas as both requires are true. + function optimisticRequireCtTrue() public { + // True. + TFHE.optimisticRequireCt(TFHE.lte(ct1, ct2)); - // Mutate state to pay for gas. - doWorkToPayGas(); - } + // True. + TFHE.optimisticRequireCt(TFHE.lte(ct1, ct2)); - // Charge full gas as we are using optimistic requires. - function optimisticRequireCtFalse() public { - // True. - TFHE.optimisticRequireCt(TFHE.lte(ct1, ct2)); + // Mutate state to pay for gas. + doWorkToPayGas(); + } - // False. - TFHE.optimisticRequireCt(TFHE.lt(ct1, ct2)); + // Charge full gas as we are using optimistic requires. + function optimisticRequireCtFalse() public { + // True. + TFHE.optimisticRequireCt(TFHE.lte(ct1, ct2)); - // Mutate state to pay for gas - we will pay for it, because we are using optimistic requires. - doWorkToPayGas(); - } + // False. + TFHE.optimisticRequireCt(TFHE.lt(ct1, ct2)); - // Charge less than full gas, because the non-optimistic ciphertext require aborts early. - function requireCtFalse() public { - // True. - TFHE.requireCt(TFHE.lte(ct1, ct2)); + // Mutate state to pay for gas - we will pay for it, because we are using optimistic requires. + doWorkToPayGas(); + } - // False. - TFHE.requireCt(TFHE.lt(ct1, ct2)); + // Charge less than full gas, because the non-optimistic ciphertext require aborts early. + function requireCtFalse() public { + // True. + TFHE.requireCt(TFHE.lte(ct1, ct2)); - // Try to mutate state to pay for gas - we won't reach that point due to the false require. - doWorkToPayGas(); - } + // False. + TFHE.requireCt(TFHE.lt(ct1, ct2)); - // Must behave as requireCtFalse() in terms of gas. - // Since gas estimation would always fail, call it without it by providing a gas value and observe transaction gas usage. - function requireFalse() public { - // False. - require(false); + // Try to mutate state to pay for gas - we won't reach that point due to the false require. + doWorkToPayGas(); + } - // Try to mutate state to pay for gas - we won't reach that point due to the false require. - doWorkToPayGas(); - } + // Must behave as requireCtFalse() in terms of gas. + // Since gas estimation would always fail, call it without it by providing a gas value and observe transaction gas usage. + function requireFalse() public { + // False. + require(false); + + // Try to mutate state to pay for gas - we won't reach that point due to the false require. + doWorkToPayGas(); + } } diff --git a/examples/SmallEncryptedERC20.sol b/examples/SmallEncryptedERC20.sol index ee26789..3a457ab 100644 --- a/examples/SmallEncryptedERC20.sol +++ b/examples/SmallEncryptedERC20.sol @@ -2,117 +2,144 @@ pragma solidity >=0.8.13 <0.9.0; -import './abstract/EIP712WithModifier.sol'; +import "./abstract/EIP712WithModifier.sol"; -import '../lib/TFHE.sol'; +import "../lib/TFHE.sol"; contract SmallEncryptedERC20 is EIP712WithModifier { - euint8 public totalSupply; - string public name = 'Naraggara'; // City of Zama's battle - string public symbol = 'NARA'; - uint8 public decimals = 18; + euint8 public totalSupply; + string public name = "Naraggara"; // City of Zama's battle + string public symbol = "NARA"; + uint8 public decimals = 18; - // A mapping from address to an encrypted balance. - mapping(address => euint8) internal balances; + // A mapping from address to an encrypted balance. + mapping(address => euint8) internal balances; - // A mapping of the form mapping(owner => mapping(spender => allowance)). - mapping(address => mapping(address => euint8)) internal allowances; + // A mapping of the form mapping(owner => mapping(spender => allowance)). + mapping(address => mapping(address => euint8)) internal allowances; - // The owner of the contract. - address internal contractOwner; + // The owner of the contract. + address internal contractOwner; - constructor() EIP712WithModifier('Authorization token', '1') { - contractOwner = msg.sender; - } + constructor() EIP712WithModifier("Authorization token", "1") { + contractOwner = msg.sender; + } - // Sets the balance of the owner to the given encrypted balance. - function mint(bytes calldata encryptedAmount) public onlyContractOwner { - euint8 amount = TFHE.asEuint8(encryptedAmount); - balances[contractOwner] = amount; - totalSupply = TFHE.add(totalSupply, amount); - } + // Sets the balance of the owner to the given encrypted balance. + function mint(bytes calldata encryptedAmount) public onlyContractOwner { + euint8 amount = TFHE.asEuint8(encryptedAmount); + balances[contractOwner] = amount; + totalSupply = TFHE.add(totalSupply, amount); + } - // Transfers an encrypted amount from the message sender address to the `to` address. - function transfer(address to, bytes calldata encryptedAmount) public { - transfer(to, TFHE.asEuint8(encryptedAmount)); - } + // Transfers an encrypted amount from the message sender address to the `to` address. + function transfer(address to, bytes calldata encryptedAmount) public { + transfer(to, TFHE.asEuint8(encryptedAmount)); + } - // Transfers an amount from the message sender address to the `to` address. - function transfer(address to, euint8 amount) internal { - _transfer(msg.sender, to, amount); - } + // Transfers an amount from the message sender address to the `to` address. + function transfer(address to, euint8 amount) internal { + _transfer(msg.sender, to, amount); + } - function getTotalSupply( - bytes32 publicKey, - bytes calldata signature - ) public view onlyContractOwner onlySignedPublicKey(publicKey, signature) returns (bytes memory) { - return TFHE.reencrypt(totalSupply, publicKey); - } + function getTotalSupply( + bytes32 publicKey, + bytes calldata signature + ) + public + view + onlyContractOwner + onlySignedPublicKey(publicKey, signature) + returns (bytes memory) + { + return TFHE.reencrypt(totalSupply, publicKey); + } - // Returns the balance of the caller under their public FHE key. - // The FHE public key is automatically determined based on the origin of the call. - function balanceOf( - bytes32 publicKey, - bytes calldata signature - ) public view onlySignedPublicKey(publicKey, signature) returns (bytes memory) { - return TFHE.reencrypt(balances[msg.sender], publicKey); - } + // Returns the balance of the caller under their public FHE key. + // The FHE public key is automatically determined based on the origin of the call. + function balanceOf( + bytes32 publicKey, + bytes calldata signature + ) + public + view + onlySignedPublicKey(publicKey, signature) + returns (bytes memory) + { + return TFHE.reencrypt(balances[msg.sender], publicKey); + } - // Sets the `encryptedAmount` as the allowance of `spender` over the caller's tokens. - function approve(address spender, bytes calldata encryptedAmount) public { - address owner = msg.sender; - _approve(owner, spender, TFHE.asEuint8(encryptedAmount)); - } + // Sets the `encryptedAmount` as the allowance of `spender` over the caller's tokens. + function approve(address spender, bytes calldata encryptedAmount) public { + address owner = msg.sender; + _approve(owner, spender, TFHE.asEuint8(encryptedAmount)); + } - // Returns the remaining number of tokens that `spender` is allowed to spend - // on behalf of the caller. The returned ciphertext is under the caller public FHE key. - function allowance( - address spender, - bytes32 publicKey, - bytes calldata signature - ) public view onlySignedPublicKey(publicKey, signature) returns (bytes memory) { - address owner = msg.sender; - return TFHE.reencrypt(_allowance(owner, spender), publicKey); - } + // Returns the remaining number of tokens that `spender` is allowed to spend + // on behalf of the caller. The returned ciphertext is under the caller public FHE key. + function allowance( + address spender, + bytes32 publicKey, + bytes calldata signature + ) + public + view + onlySignedPublicKey(publicKey, signature) + returns (bytes memory) + { + address owner = msg.sender; + return TFHE.reencrypt(_allowance(owner, spender), publicKey); + } - // Transfers `encryptedAmount` tokens using the caller's allowance. - function transferFrom(address from, address to, bytes calldata encryptedAmount) public { - transferFrom(from, to, TFHE.asEuint8(encryptedAmount)); - } + // Transfers `encryptedAmount` tokens using the caller's allowance. + function transferFrom( + address from, + address to, + bytes calldata encryptedAmount + ) public { + transferFrom(from, to, TFHE.asEuint8(encryptedAmount)); + } - // Transfers `amount` tokens using the caller's allowance. - function transferFrom(address from, address to, euint8 amount) public { - address spender = msg.sender; - _updateAllowance(from, spender, amount); - _transfer(from, to, amount); - } + // Transfers `amount` tokens using the caller's allowance. + function transferFrom(address from, address to, euint8 amount) public { + address spender = msg.sender; + _updateAllowance(from, spender, amount); + _transfer(from, to, amount); + } - function _approve(address owner, address spender, euint8 amount) internal { - allowances[owner][spender] = amount; - } + function _approve(address owner, address spender, euint8 amount) internal { + allowances[owner][spender] = amount; + } - function _allowance(address owner, address spender) internal view returns (euint8) { - return allowances[owner][spender]; - } + function _allowance( + address owner, + address spender + ) internal view returns (euint8) { + return allowances[owner][spender]; + } - function _updateAllowance(address owner, address spender, euint8 amount) internal { - euint8 currentAllowance = _allowance(owner, spender); - TFHE.requireCt(TFHE.lte(amount, currentAllowance)); - _approve(owner, spender, TFHE.sub(currentAllowance, amount)); - } + function _updateAllowance( + address owner, + address spender, + euint8 amount + ) internal { + euint8 currentAllowance = _allowance(owner, spender); + TFHE.requireCt(TFHE.lte(amount, currentAllowance)); + _approve(owner, spender, TFHE.sub(currentAllowance, amount)); + } - // Transfers an encrypted amount. - function _transfer(address from, address to, euint8 amount) internal { - // Make sure the sender has enough tokens. - TFHE.requireCt(TFHE.lte(amount, balances[from])); + // Transfers an encrypted amount. + function _transfer(address from, address to, euint8 amount) internal { + // Make sure the sender has enough tokens. + TFHE.requireCt(TFHE.lte(amount, balances[from])); - // Add to the balance of `to` and subract from the balance of `from`. - balances[to] = TFHE.add(balances[to], amount); - balances[from] = TFHE.sub(balances[from], amount); - } + // Add to the balance of `to` and subract from the balance of `from`. + balances[to] = TFHE.add(balances[to], amount); + balances[from] = TFHE.sub(balances[from], amount); + } - modifier onlyContractOwner() { - require(msg.sender == contractOwner); - _; - } + modifier onlyContractOwner() { + require(msg.sender == contractOwner); + _; + } } diff --git a/examples/abstract/EIP712WithModifier.sol b/examples/abstract/EIP712WithModifier.sol index 92a5571..296254c 100644 --- a/examples/abstract/EIP712WithModifier.sol +++ b/examples/abstract/EIP712WithModifier.sol @@ -2,16 +2,26 @@ pragma solidity >=0.8.13 <0.9.0; -import '../../node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol'; -import '../../node_modules/@openzeppelin/contracts/utils/cryptography/EIP712.sol'; +import "../../node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import "../../node_modules/@openzeppelin/contracts/utils/cryptography/EIP712.sol"; abstract contract EIP712WithModifier is EIP712 { - constructor(string memory name, string memory version) EIP712(name, version) {} + constructor( + string memory name, + string memory version + ) EIP712(name, version) {} - modifier onlySignedPublicKey(bytes32 publicKey, bytes memory signature) { - bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(keccak256('Reencrypt(bytes32 publicKey)'), publicKey))); - address signer = ECDSA.recover(digest, signature); - require(signer == msg.sender, 'EIP712 signer and transaction signer do not match'); - _; - } + modifier onlySignedPublicKey(bytes32 publicKey, bytes memory signature) { + bytes32 digest = _hashTypedDataV4( + keccak256( + abi.encode(keccak256("Reencrypt(bytes32 publicKey)"), publicKey) + ) + ); + address signer = ECDSA.recover(digest, signature); + require( + signer == msg.sender, + "EIP712 signer and transaction signer do not match" + ); + _; + } } diff --git a/lib/Impl.sol b/lib/Impl.sol index af560c3..37f2faf 100644 --- a/lib/Impl.sol +++ b/lib/Impl.sol @@ -27,7 +27,16 @@ library Impl { // Call the add precompile. uint256 precompile = Precompiles.Add; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + output, + outputLen + ) + ) { revert(0, 0) } } @@ -52,7 +61,16 @@ library Impl { // Call the sub precompile. uint256 precompile = Precompiles.Subtract; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + output, + outputLen + ) + ) { revert(0, 0) } } @@ -77,7 +95,16 @@ library Impl { // Call the mul precompile. uint256 precompile = Precompiles.Multiply; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + output, + outputLen + ) + ) { revert(0, 0) } } @@ -87,7 +114,10 @@ library Impl { // Evaluate `lhs <= rhs` on the given ciphertexts and, if successful, return the resulting ciphertext. // If successful, the resulting ciphertext is automatically verified. - function lte(uint256 lhs, uint256 rhs) internal view returns (uint256 result) { + function lte( + uint256 lhs, + uint256 rhs + ) internal view returns (uint256 result) { bytes32[2] memory input; input[0] = bytes32(lhs); input[1] = bytes32(rhs); @@ -99,7 +129,16 @@ library Impl { // Call the lte precompile. uint256 precompile = Precompiles.LessThanOrEqual; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + output, + outputLen + ) + ) { revert(0, 0) } } @@ -109,7 +148,10 @@ library Impl { // Evaluate `lhs < rhs` on the given ciphertexts and, if successful, return the resulting ciphertext. // If successful, the resulting ciphertext is automatically verified. - function lt(uint256 lhs, uint256 rhs) internal view returns (uint256 result) { + function lt( + uint256 lhs, + uint256 rhs + ) internal view returns (uint256 result) { bytes32[2] memory input; input[0] = bytes32(lhs); input[1] = bytes32(rhs); @@ -121,7 +163,16 @@ library Impl { // Call the lte precompile. uint256 precompile = Precompiles.LessThan; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, output, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + output, + outputLen + ) + ) { revert(0, 0) } } @@ -132,7 +183,11 @@ library Impl { // If `control`'s value is 1, the resulting value is the same value as `ifTrue`. // If `control`'s value is 0, the resulting value is the same value as `ifFalse`. // If successful, the resulting ciphertext is automatically verified. - function cmux(uint256 control, uint256 ifTrue, uint256 ifFalse) internal view returns (uint256 result) { + function cmux( + uint256 control, + uint256 ifTrue, + uint256 ifFalse + ) internal view returns (uint256 result) { // result = (ifTrue - ifFalse) * control + ifFalse bytes32[2] memory input; @@ -145,7 +200,16 @@ library Impl { uint256 precompile = Precompiles.Subtract; bytes32[1] memory subOutput; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, subOutput, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + subOutput, + outputLen + ) + ) { revert(0, 0) } } @@ -156,7 +220,16 @@ library Impl { precompile = Precompiles.Multiply; bytes32[1] memory mulOutput; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, mulOutput, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + mulOutput, + outputLen + ) + ) { revert(0, 0) } } @@ -167,14 +240,23 @@ library Impl { precompile = Precompiles.Add; bytes32[1] memory addOutput; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, addOutput, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + addOutput, + outputLen + ) + ) { revert(0, 0) } } result = uint256(addOutput[0]); } - + // Optimistically requires that the `ciphertext` is true. // // This function does not evaluate the given `ciphertext` at the time of the call. @@ -201,7 +283,10 @@ library Impl { } } - function reencrypt(uint256 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) { + function reencrypt( + uint256 ciphertext, + bytes32 publicKey + ) internal view returns (bytes memory reencrypted) { bytes32[2] memory input; input[0] = bytes32(ciphertext); input[1] = publicKey; @@ -212,7 +297,16 @@ library Impl { // Call the reencrypt precompile. uint256 precompile = Precompiles.Reencrypt; assembly { - if iszero(staticcall(gas(), precompile, input, inputLen, reencrypted, reencryptedSize)) { + if iszero( + staticcall( + gas(), + precompile, + input, + inputLen, + reencrypted, + reencryptedSize + ) + ) { revert(0, 0) } } @@ -232,7 +326,16 @@ library Impl { uint256 precompile = Precompiles.Verify; assembly { // jump over the 32-bit `size` field of the `bytes` data structure of the `input` to read actual bytes - if iszero(staticcall(gas(), precompile, add(input, 32), inputLen, output, outputLen)) { + if iszero( + staticcall( + gas(), + precompile, + add(input, 32), + inputLen, + output, + outputLen + ) + ) { revert(0, 0) } } diff --git a/lib/TFHE.sol b/lib/TFHE.sol index daecad4..28f1a28 100644 --- a/lib/TFHE.sol +++ b/lib/TFHE.sol @@ -66,23 +66,59 @@ library TFHE { return euint32.wrap(Impl.lt(euint32.unwrap(a), euint32.unwrap(b))); } - function cmux(euint8 control, euint8 a, euint8 b) internal view returns (euint8) { - return euint8.wrap(Impl.cmux(euint8.unwrap(control), euint8.unwrap(a), euint8.unwrap(b))); + function cmux( + euint8 control, + euint8 a, + euint8 b + ) internal view returns (euint8) { + return + euint8.wrap( + Impl.cmux( + euint8.unwrap(control), + euint8.unwrap(a), + euint8.unwrap(b) + ) + ); } - function cmux(euint8 control, euint16 a, euint16 b) internal view returns (euint16) { - return euint16.wrap(Impl.cmux(euint8.unwrap(control), euint16.unwrap(a), euint16.unwrap(b))); + function cmux( + euint8 control, + euint16 a, + euint16 b + ) internal view returns (euint16) { + return + euint16.wrap( + Impl.cmux( + euint8.unwrap(control), + euint16.unwrap(a), + euint16.unwrap(b) + ) + ); } - function cmux(euint8 control, euint32 a, euint32 b) internal view returns (euint32) { - return euint32.wrap(Impl.cmux(euint8.unwrap(control), euint32.unwrap(a), euint32.unwrap(b))); + function cmux( + euint8 control, + euint32 a, + euint32 b + ) internal view returns (euint32) { + return + euint32.wrap( + Impl.cmux( + euint8.unwrap(control), + euint32.unwrap(a), + euint32.unwrap(b) + ) + ); } function asEuint8(bytes memory ciphertext) internal view returns (euint8) { return euint8.wrap(Impl.verify(ciphertext, Common.euint8_t)); } - function reencrypt(euint8 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) { + function reencrypt( + euint8 ciphertext, + bytes32 publicKey + ) internal view returns (bytes memory reencrypted) { return Impl.reencrypt(euint8.unwrap(ciphertext), publicKey); } @@ -94,11 +130,16 @@ library TFHE { Impl.optimisticRequireCt(euint8.unwrap(ciphertext)); } - function asEuint16(bytes memory ciphertext) internal view returns (euint16) { + function asEuint16( + bytes memory ciphertext + ) internal view returns (euint16) { return euint16.wrap(Impl.verify(ciphertext, Common.euint16_t)); } - function reencrypt(euint16 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) { + function reencrypt( + euint16 ciphertext, + bytes32 publicKey + ) internal view returns (bytes memory reencrypted) { return Impl.reencrypt(euint16.unwrap(ciphertext), publicKey); } @@ -110,11 +151,16 @@ library TFHE { Impl.optimisticRequireCt(euint16.unwrap(ciphertext)); } - function asEuint32(bytes memory ciphertext) internal view returns (euint32) { + function asEuint32( + bytes memory ciphertext + ) internal view returns (euint32) { return euint32.wrap(Impl.verify(ciphertext, Common.euint32_t)); } - function reencrypt(euint32 ciphertext, bytes32 publicKey) internal view returns (bytes memory reencrypted) { + function reencrypt( + euint32 ciphertext, + bytes32 publicKey + ) internal view returns (bytes memory reencrypted) { return Impl.reencrypt(euint32.unwrap(ciphertext), publicKey); }