├── LICENSE.md ├── README.md ├── contracts ├── ERC20Interface.sol ├── OpsCoin.sol ├── OpsCoinShield.sol ├── SafeMath.sol ├── TokenShield.sol └── Verifier.sol └── screenshot.png /LICENSE.md: -------------------------------------------------------------------------------- 1 | Note that the below applies to code originated by EYGS LLP only. Source code modules originated by 2 | others, and included in this repository, carry license conditions. We have included license 3 | information in the relevant modules. 4 | 5 | While not required, we appreciate a small attribution to the effect that "This software makes use of 6 | the Nightfall code originally created by EY". 7 | 8 |

9 | 11 | CC0 16 | 17 |
18 | To the extent possible under law, 19 | 20 | EYGS LLP 21 | has waived all copyright and related or neighboring rights to 22 | this work. 23 |

24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Are Secure Private Transactions on Public Blockchains Really Possible? Help Us Find Out. 2 | 3 | The world of blockchain technology is really split in two today. On the one hand, there are big public networks like Ethereum with billions of dollars of investment and millions of users. On the other hand, there are thousands of private blockchains run by enterprises. Enterprise use of public blockchains is very limited because there is no privacy for business transactions or contracts. 4 | 5 | Our goal at EY is to bridge this gap between public and private blockchains by enabling secure private transactions over public networks using Zero Knowledge Proofs. And we think we have done it. 6 | 7 | What we are showing (below) is a supply chain solution based on EY’s Ops Chain platform that is used today by private networks for managing and tracking supply chain operations, but all executed on the public network with zero knowledge proofs. What you see here is a prototype (We call it the Ops Chain Public Edition Prototype) and we’d like your help in finding the holes in our work. This platform leverages Nightfall to provide privacy 8 | 9 | **Can you tell us who sold what to whom and how much it cost?** 10 | 11 | ## Aim of the Grand Challenge 12 | 13 | There are products travelling through a supply chain process from supplier to manufacturer to pharmacy with the help of a transporter. The aim is to find how many of what products are sent to manufacturer and then how many products of each does the manufacturer assemble to create how many number of final products in order to send to Pharmacy. Also who has paid how much to whom for each of the services provided. The diagram below provides a good illustration of the question. 14 | 15 | ![screenshot](screenshot.png "Question") 16 | 17 | ## What are the ZKP Protocols used and what do they accomplish? 18 | 19 | In this work, we use an approach that provides a solution using Zero Knowledge Proofs. Any asset that needs to be tracked is tokenised into a non-fungible token commitment while any payment that needs to be made against this asset is tokenised into a fungible token commitment. Processes are built over the movement of these token commitments using business logic executed through smart contracts. But to maintain privacy for business operations regarding what asset has been transferred between which two parties and how much was paid for it, we use 7 different ZKP protocols. 20 | 21 | The zero knowledge proofs for these protocols are generated off chain while the verification of these proofs happen on chain. 22 | 23 | The protocols are: 24 | 25 | * **Fungible Token Mint** : Creates a fungible token commitment on the blockchain that holds value equivalent to the amount deposited to the `OpsCoinShield.sol` smart contract. The amount deposited can be in the form of any ERC20 token such as Ether. In this example, we use Dollar Tokens created using `OpsCoin.sol` for depositing. 26 | Privacy - Minting is intended to be done from a known address and the value of minted token commitment is known. From the moment the token commitment is minted, nothing about what happened to it after is revealed. 27 | 28 | * **Fungible Token Transfer** : Any fungible token commitment can be transferred from an owner to a recipient. 29 | Privacy - Transferring is intended to be done from an anonymous address of the sender, the recipient as well as the value of the token commitment transferred isn't revealed. 30 | 31 | * **Fungible Token Burn** : Enables to withdraw the value held in a fungible token commitment back into an ERC20 token such as Ether or Dollar Tokens. In this example, Dollar tokens created using `OpsCoin.sol` will be withdrawn. 32 | Privacy - Burning is intended to be done from a public address and the amount withdrawn is known. 33 | 34 | * **Non-Fungible Token Mint** : Creates a Non-Fungible token commitment on the blockchain that represents an asset by holding the asset hash derived from storing the asset's meta data on swarm. 35 | Privacy - Minting is intended to be from a known address, whilst the asset represented by the asset hash in the token commitment isn't revealed. 36 | 37 | * **Non-Fungible Token Transfer** : Any Non-Fungible token commitment can be transferred from an owner to a recipient. 38 | Privacy - Transferring is intended to be done from an anonymous address of the sender. The recipient as well as the asset held by the token commitment aren't revealed. 39 | 40 | * **Non-Fungible Token Join** : The assets stored on swarm are hierarchical in nature. This protocol enables an asset token commitment to be joined with another asset token commitment. The joined asset along with it's meta data is stored in swarm and a new token commitment is created on the blockchain using the resulting hash that represents this combined asset on the swarm while nullifying the original individual token commitments. Once joined, the new token commitment can be kept by the current owner or transferred to another recipient. 41 | Privacy - The sender, the recipient, the first asset that was joined, the second asset that was joined and the joined asset are not revealed. 42 | 43 | * **Non-Fungible Token Split** : This protocol enables an asset token commitment to be split into multiple asset token commitments. These new assets along with their meta data are stored in swarm and new token commitments are created on the blockchain using the resulting hashes that represent these split assets on the swarm while nullifying the original token commitment. Once split, the new token commitments can be kept by the current owner or transferred to another recipient. 44 | Privacy - The sender, the recipient, the original asset split and the split assets are not revealed. 45 | 46 | These protocols are used in the movement of all assets and payments on a public blockchain to ensure privacy. 47 | 48 | ## Contracts 49 | 50 | * *Verifier.sol* : This smart contract verifies the zero knowledge proofs for all protocols specified, returning a boolean on success or failure. 51 | 52 | * *OpsCoin.sol* : This is an ERC20 token smart contract that mints Public Dollar Tokens used in this example. Private Dollar Tokens can be minted by depositing these Public Dollar Tokens to the `OpsCoinShield.sol` smart contract. Also, Private Dollar tokens can be burnt to withdraw Public Dollar Tokens 53 | using the `OpsCoinShield.sol` contract. 54 | 55 | * *OpsCoinShield.sol* : This smart contract acts as a shield by hiding the association of fungible token commitments and their nullifiers. In order to mint, transfer or burn, a transaction is sent to this smart contract which then verifies the proof by calling `Verifier.sol` and adds the relevant token commitments to the token commitments list tree and nullifiers to the nullifiers list array. 56 | 57 | * *TokenShield.sol* : This smart contract acts as a shield by hiding the association of non-fungible token commitments and their nullifiers. In order to mint, transfer, join or split, a transaction is sent to this smart contract which then verifies the proof by calling `Verifier.sol` and adds the relevant token commitments to the token commitments list tree, nullifiers to the nullifiers list array and double spend preventifiers to the double spend preventifiers list array. The double spend preventifier does what its named after, ensuring no two token commitments hold the same asset. 58 | 59 | ## Data Store 60 | 61 | **Swarm** - Swarm is used as an off chain distributed storage for asset meta data. The hash returned after storage of an asset is used to represent an asset uniquely and is used in the making of the token commitment that represents this asset. 62 | 63 | The swarm public gateway run by Ethereum foundation is used here. 64 | `https://swarm-gateways.net/` 65 | 66 | ## Information required to get you started on answering the challenge 67 | 68 | **List of Ethereum Addresses** 69 | 70 | * Coinbase - 0xad70676fdfd0e04d62a3c2d623de04dd8940c642 71 | * Supplier 1 - 0x70623d1c3a316ae22be376374e25476bd308e263 72 | * Supplier 2 - 0x5292606982b9f0db32c56686741f76d133d4c924 73 | * Transporter - 0x6b9e39a04251070a8c5db2ffc8a0ba7972a41478 74 | * Manufacturer - 0x8ae3eff37db50fac1da0d79965154d9cb7523eeb 75 | * Pharmacy - 0xf9ff784b87688e4c32c588f882caf13c6167d70f 76 | 77 | **List of Contract Addresses** 78 | 79 | * OpsCoin: 0x09b2d8b8741538abf56f47be76e37aed31f00e0d 80 | * OpsCoinShield: 0xe8d9f21b6b351c6bf993b1105497fe42d30df8bf 81 | * TokenShield: 0xeb7160d9fba402219ea868bc311e7d98688d3180 82 | * FT Mint & Burn Verifier: 0x6b98f17897a5086ff3e7357290b36022798d8850 83 | * FT Transfer Verifier: 0xcb4ccc716abe1b5be15870e2bfa12bf5f0b501e6 84 | * NFT Mint Verifier: 0xc5daef66b7752d9c5920c15e9e0145aa839843f3 85 | * NFT Transfer Verifier: 0x8d993aaf8e92c2027a5358b9914392050b03356d 86 | * NFT Join Verifier: 0xf8cb6400dcf523bf6397a79bccd35a6af1a88edd 87 | * NFT Split Verifier: 0xb065dcb7651b448e042a0acc729ae64d2ea3ba51 88 | 89 | **List of Anonymous Ethereum Addresses of the above players** 90 | 91 | * 0x5a812eb611c72c9b94fbb6876b5f49bcc6e41945 92 | * 0xb61499e29eb7947c59ed82cf9c360d01dd9e5e0c 93 | * 0xa9f91a59b1b5d76334de878dd5dc7627ee748e35 94 | * 0x81caa8ddc289fb7310d2ce1c5e8231f1d7ee9754 95 | * 0x04734be5baa73c907527f03b0a938587f2d3ce61 96 | * 0xc767957dccc903e9969d9bd00c429547de752798 97 | * 0x2250ddc2609d2dcc525a8cdbf8ef20da0b9e408a 98 | * 0x056bd6e88c761faa26cc927ef3fcd907dea77d3b 99 | * 0x9f4abe21c039737a92c4c93715578a4bd344d4ab 100 | * 0x700ec2a9d659a7af8dd9e4c583d782f308878624 101 | * 0x5aba0f87d89f5b96eeb0c16233c357231ebb909b 102 | * 0x643e47ba204d3b4b98e8c2210c104240d37ea7aa 103 | * 0x5e84ed0071e5df742a8052411d5bd292f9294cff 104 | * 0x16a0dcfaf9a495f60afa5aaa298af662f4b97e98 105 | 106 | **Other relevant information** 107 | * First Block: 6477282 108 | * Last Block: 6485984 109 | 110 | * First Tx Hash: 0x5e608f6caca686b3a3e7a901efdfcc94bc9a593891d7b8f910273ff56f9f1822 111 | * Last Tx Hash 0x83007c1a4af2ef1487fe020c1ea2f343c552d187a2f6aac52720abbefe5470e7 112 | 113 | ### Limitations 114 | * The usage of the terms 'fungible' and 'non-fungible' for token commitments means that the token commitments hold fungible or non-fungible assets by nature. It does not imply that the token commitments are tokens themselves that are ERC20 or ERC720 standard compatible. 115 | * It can be noted that inputs to proof verification are of 64 bits in length. Generally, this length is not secure against brute force attacks. In the later iterations of this work, these will be changed to 256 bits. 116 | * Various performance improvements can be made to make the contracts more cost efficient such as using trees in place of arrays to hold nullifiers or double spend preventifiers. Upcoming iterations of the work will address this. 117 | * The maximum number of token commitments that the merkle tree currently holds is 256. The next iteration will increment this for practical use. 118 | * The usage of the term 'public dollar tokens' implies that OpsCoin is a stable coin that represents a dollar. 119 | * Some of the work for fungible token here is inspired by ZCash 120 | -------------------------------------------------------------------------------- /contracts/ERC20Interface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | /** 4 | * @title ERC20 interface 5 | * @dev see https://github.com/ethereum/EIPs/issues/20 6 | */ 7 | interface ERC20Interface { 8 | function totalSupply() external view returns (uint256); 9 | 10 | function balanceOf(address who) external view returns (uint256); 11 | 12 | function allowance(address owner, address spender) 13 | external view returns (uint256); 14 | 15 | function transfer(address to, uint256 value) external returns (bool); 16 | 17 | function approve(address spender, uint256 value) 18 | external returns (bool); 19 | 20 | function transferFrom(address from, address to, uint256 value) 21 | external returns (bool); 22 | 23 | event Transfer( 24 | address indexed from, 25 | address indexed to, 26 | uint256 value 27 | ); 28 | 29 | event Approval( 30 | address indexed owner, 31 | address indexed spender, 32 | uint256 value 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/OpsCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./ERC20Interface.sol"; 4 | import "./SafeMath.sol"; 5 | 6 | /** 7 | * @title OpsCoin V1 - ERC20 Token 8 | * 9 | * @dev Implementation of the basic standard token. 10 | * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md 11 | * Originally based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 12 | * Based on OpenZeppelin's version of the code by FirstBlood . 13 | */ 14 | 15 | contract OpsCoin is ERC20Interface { 16 | 17 | using SafeMath for uint256; 18 | 19 | string public symbol; 20 | string public name; 21 | address public owner; 22 | uint256 public totalSupply; 23 | 24 | 25 | mapping (address => uint256) private balances; 26 | mapping (address => mapping (address => uint256)) private allowed; 27 | mapping (address => mapping (address => uint)) private timeLock; 28 | 29 | 30 | constructor() { 31 | symbol = "OPS"; 32 | name = "EY OpsCoin"; 33 | totalSupply = 1000000; 34 | owner = msg.sender; 35 | balances[owner] = totalSupply; 36 | emit Transfer(address(0), owner, totalSupply); 37 | } 38 | 39 | //only owner modifier 40 | modifier onlyOwner () { 41 | require(msg.sender == owner); 42 | _; 43 | } 44 | 45 | /** 46 | self destruct added by westlad 47 | */ 48 | function close() public onlyOwner { 49 | selfdestruct(owner); 50 | } 51 | 52 | /** 53 | * @dev Gets the balance of the specified address. 54 | * @param _address The address to query the balance of. 55 | * @return An uint256 representing the amount owned by the passed address. 56 | */ 57 | function balanceOf(address _address) public view returns (uint256) { 58 | return balances[_address]; 59 | } 60 | 61 | /** 62 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 63 | * @param _owner address The address which owns the funds. 64 | * @param _spender address The address which will spend the funds. 65 | * @return A uint256 specifying the amount of tokens still available for the spender. 66 | */ 67 | function allowance(address _owner, address _spender) public view returns (uint256) 68 | { 69 | return allowed[_owner][_spender]; 70 | } 71 | 72 | /** 73 | * @dev Total number of tokens in existence 74 | */ 75 | function totalSupply() public view returns (uint256) { 76 | return totalSupply; 77 | } 78 | 79 | 80 | /** 81 | * @dev Internal function that mints an amount of the token and assigns it to 82 | * an account. This encapsulates the modification of balances such that the 83 | * proper events are emitted. 84 | * @param _account The account that will receive the created tokens. 85 | * @param _amount The amount that will be created. 86 | */ 87 | function mint(address _account, uint256 _amount) public { 88 | require(_account != 0); 89 | require(_amount > 0); 90 | totalSupply = totalSupply.add(_amount); 91 | balances[_account] = balances[_account].add(_amount); 92 | emit Transfer(address(0), _account, _amount); 93 | } 94 | 95 | /** 96 | * @dev Internal function that burns an amount of the token of a given 97 | * account. 98 | * @param _account The account whose tokens will be burnt. 99 | * @param _amount The amount that will be burnt. 100 | */ 101 | function burn(address _account, uint256 _amount) public { 102 | require(_account != 0); 103 | require(_amount <= balances[_account]); 104 | 105 | totalSupply = totalSupply.sub(_amount); 106 | balances[_account] = balances[_account].sub(_amount); 107 | emit Transfer(_account, address(0), _amount); 108 | } 109 | 110 | /** 111 | * @dev Internal function that burns an amount of the token of a given 112 | * account, deducting from the sender's allowance for said account. Uses the 113 | * internal burn function. 114 | * @param _account The account whose tokens will be burnt. 115 | * @param _amount The amount that will be burnt. 116 | */ 117 | function burnFrom(address _account, uint256 _amount) public { 118 | require(_amount <= allowed[_account][msg.sender]); 119 | 120 | allowed[_account][msg.sender] = allowed[_account][msg.sender].sub(_amount); 121 | emit Approval(_account, msg.sender, allowed[_account][msg.sender]); 122 | burn(_account, _amount); 123 | } 124 | 125 | /** 126 | * @dev Transfer token for a specified address 127 | * @param _to The address to transfer to. 128 | * @param _value The amount to be transferred. 129 | */ 130 | function transfer(address _to, uint256 _value) public returns (bool) { 131 | require(_value <= balances[msg.sender]); 132 | require(_to != address(0)); 133 | 134 | balances[msg.sender] = balances[msg.sender].sub(_value); 135 | balances[_to] = balances[_to].add(_value); 136 | emit Transfer(msg.sender, _to, _value); 137 | return true; 138 | } 139 | 140 | /** 141 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 142 | * @param _spender The address which will spend the funds. 143 | * @param _value The amount of tokens to be spent. 144 | */ 145 | function approve(address _spender, uint256 _value) public returns (bool) { 146 | require(_spender != address(0)); 147 | 148 | allowed[msg.sender][_spender] = _value; 149 | emit Approval(msg.sender, _spender, _value); 150 | return true; 151 | } 152 | 153 | /** 154 | * @dev Approve the passed address to spend the specified amount of tokens after a specfied amount of time on behalf of msg.sender. 155 | * @param _spender The address which will spend the funds. 156 | * @param _value The amount of tokens to be spent. 157 | * @param _timeLockTill The time until when this amount cannot be withdrawn 158 | * @notice © Copyright 2018 EYGS LLP and/or other members of the global Ernst & Young/EY network; pat. pending. 159 | */ 160 | function approveAt(address _spender, uint256 _value, uint _timeLockTill) public returns (bool) { 161 | 162 | require(_spender != address(0)); 163 | 164 | allowed[msg.sender][_spender] = _value; 165 | timeLock[msg.sender][_spender] = _timeLockTill; 166 | emit Approval(msg.sender, _spender, _value); 167 | return true; 168 | } 169 | 170 | /** 171 | * @dev Transfer tokens from one address to another 172 | * @param _from address The address which you want to send tokens from 173 | * @param _to address The address which you want to transfer to 174 | * @param _value uint256 the amount of tokens to be transferred 175 | */ 176 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool) 177 | { 178 | require(_value <= balances[_from]); 179 | require(_value <= allowed[_from][msg.sender]); 180 | require(_to != address(0)); 181 | 182 | balances[_from] = balances[_from].sub(_value); 183 | balances[_to] = balances[_to].add(_value); 184 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 185 | emit Transfer(_from, _to, _value); 186 | return true; 187 | } 188 | 189 | /** 190 | * @dev Transfer tokens from one address to another 191 | * @param _from address The address which you want to send tokens from 192 | * @param _to address The address which you want to transfer to 193 | * @param _value uint256 the amount of tokens to be transferred 194 | * @notice © Copyright 2018 EYGS LLP and/or other members of the global Ernst & Young/EY network; pat. pending. 195 | */ 196 | function transferFromAt(address _from, address _to, uint256 _value) public returns (bool) 197 | { 198 | require(_value <= balances[_from]); 199 | require(_value <= allowed[_from][msg.sender]); 200 | require(_to != address(0)); 201 | require(block.timestamp > timeLock[_from][msg.sender]); 202 | 203 | balances[_from] = balances[_from].sub(_value); 204 | balances[_to] = balances[_to].add(_value); 205 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 206 | emit Transfer(_from, _to, _value); 207 | return true; 208 | } 209 | 210 | /** 211 | * @dev Increase the amount of tokens that an owner allowed to a spender. 212 | * approve should be called when allowed_[_spender] == 0. To increment 213 | * allowed value is better to use this function to avoid 2 calls (and wait until 214 | * the first transaction is mined) 215 | * @param _spender The address which will spend the funds. 216 | * @param _addedValue The amount of tokens to increase the allowance by. 217 | */ 218 | function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) 219 | { 220 | require(_spender != address(0)); 221 | 222 | allowed[msg.sender][_spender] = (allowed[msg.sender][_spender].add(_addedValue)); 223 | emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 224 | return true; 225 | } 226 | 227 | /** 228 | * @dev Decrease the amount of tokens that an owner allowed to a spender. 229 | * approve should be called when allowed_[_spender] == 0. To decrement 230 | * allowed value is better to use this function to avoid 2 calls (and wait until 231 | * the first transaction is mined) 232 | * @param _spender The address which will spend the funds. 233 | * @param _subtractedValue The amount of tokens to decrease the allowance by. 234 | */ 235 | function decreaseAllowance(address _spender, uint256 _subtractedValue) public returns (bool) 236 | { 237 | require(_spender != address(0)); 238 | 239 | allowed[msg.sender][_spender] = (allowed[msg.sender][_spender].sub(_subtractedValue)); 240 | emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 241 | return true; 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /contracts/OpsCoinShield.sol: -------------------------------------------------------------------------------- 1 | /** 2 | @notice © Copyright 2018 EYGS LLP and/or other members of the global Ernst & Young/EY network; pat. pending. 3 | 4 | @title OpsCoinShield V1 5 | 6 | Contract to enable the management of ZKSnark-hidden coin transactions. Currently it can 7 | only cope with commitments that are 8 bytes long. A normal tokenhash is 32 bytes long 8 | TODO - need to cope with the other 24 bytes and have a deeper Merkle tree 9 | */ 10 | 11 | pragma solidity ^0.4.19; 12 | import "./OpsCoin.sol"; 13 | contract Verifier{ 14 | function verifyTx( 15 | uint[2], 16 | uint[2], 17 | uint[2][2], 18 | uint[2], 19 | uint[2], 20 | uint[2], 21 | uint[2], 22 | uint[2], 23 | address 24 | ) public pure returns (bool){} 25 | function getInputBits(uint, address) public view returns(bytes8){} 26 | } 27 | 28 | contract OpsCoinShield{ 29 | address public owner; 30 | bytes8[merkleWidth] ns; //store spent token nullifiers 31 | uint constant merkleWidth = 256; 32 | uint constant merkleDepth = 9; 33 | uint constant lastRow = merkleDepth-1; 34 | uint private balance = 0; 35 | bytes8[merkleWidth] private zs; //array holding the commitments. Basically the bottom row of the merkle tree 36 | uint private zCount; //remember the number of commitments we hold 37 | uint private nCount; //remember the number of commitments we spent 38 | bytes8[] private roots; //holds each root we've calculated so that we can pull the one relevant to the prover 39 | uint private currentRootIndex; //holds the index for the current root so that the 40 | //prover can provide it later and this contract can look up the relevant root 41 | Verifier private mv; //the verification smart contract that the mint function uses 42 | Verifier private sv; //the verification smart contract that the transfer function uses 43 | OpsCoin private ops; //the OpsCoin ERC20 token contract 44 | struct Proof { //recast this as a struct because otherwise, as a set of local variable, it takes too much stack space 45 | uint[2] a; 46 | uint[2] a_p; 47 | uint[2][2] b; 48 | uint[2] b_p; 49 | uint[2] c; 50 | uint[2] c_p; 51 | uint[2] h; 52 | uint[2] k; 53 | } 54 | //Proof proof; //not used - proof is now set per address 55 | mapping(address => Proof) private proofs; 56 | 57 | constructor(address mintVerifier, address transferVerifier, address opsCoin) public { 58 | // TODO - you can get a way with a single, generic verifier. 59 | owner = msg.sender; 60 | mv = Verifier(mintVerifier); 61 | sv = Verifier(transferVerifier); 62 | ops = OpsCoin(opsCoin); 63 | } 64 | 65 | //only owner modifier 66 | modifier onlyOwner () { 67 | require(msg.sender == owner); 68 | _; 69 | } 70 | 71 | /** 72 | self destruct added by westlad 73 | */ 74 | function close() public onlyOwner { 75 | selfdestruct(owner); 76 | } 77 | 78 | 79 | function getMintVerifier() public view returns(address){ 80 | return address(mv); 81 | } 82 | 83 | function getTransferVerifier() public view returns(address){ 84 | return address(sv); 85 | } 86 | 87 | function getOpsCoin() public view returns(address){ 88 | return address(ops); 89 | } 90 | 91 | /** 92 | The mint function accepts opscoin and creates the same amount as a commitment. 93 | */ 94 | function mint(uint amount) public { 95 | //first, verify the proof 96 | 97 | bool result = mv.verifyTx( 98 | proofs[msg.sender].a, 99 | proofs[msg.sender].a_p, 100 | proofs[msg.sender].b, 101 | proofs[msg.sender].b_p, 102 | proofs[msg.sender].c, 103 | proofs[msg.sender].c_p, 104 | proofs[msg.sender].h, 105 | proofs[msg.sender].k, 106 | msg.sender); 107 | 108 | require(result); //the proof must check out 109 | //transfer OPS from the sender to this contract 110 | ops.transferFrom(msg.sender, address(this), amount); 111 | //save the commitments 112 | bytes8 z = mv.getInputBits(64, msg.sender);//recover the input params from MintVerifier 113 | zs[zCount++] = z; //add the token 114 | require(uint(mv.getInputBits(0, msg.sender))==amount); //check we've been correctly paid 115 | bytes8 root = merkle(0,0); //work out the Merkle root as it's now different 116 | currentRootIndex = roots.push(root)-1; //and save it to the list 117 | } 118 | 119 | /** 120 | The transfer function transfers a commitment to a new owner 121 | */ 122 | function transfer() public { 123 | //verification contract 124 | bool result = sv.verifyTx( 125 | proofs[msg.sender].a, 126 | proofs[msg.sender].a_p, 127 | proofs[msg.sender].b, 128 | proofs[msg.sender].b_p, 129 | proofs[msg.sender].c, 130 | proofs[msg.sender].c_p, 131 | proofs[msg.sender].h, 132 | proofs[msg.sender].k, 133 | msg.sender); 134 | require(result); //the proof must verify. The spice must flow. 135 | 136 | bytes8 nc = sv.getInputBits(0, msg.sender); 137 | bytes8 nd = sv.getInputBits(64, msg.sender); 138 | bytes8 ze = sv.getInputBits(128, msg.sender); 139 | bytes8 zf = sv.getInputBits(192, msg.sender); 140 | for (uint i=0; i 0); // Solidity only automatically asserts when dividing by 0 31 | uint256 c = a / b; 32 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 33 | 34 | return c; 35 | } 36 | 37 | /** 38 | * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). 39 | */ 40 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 41 | require(b <= a); 42 | uint256 c = a - b; 43 | 44 | return c; 45 | } 46 | 47 | /** 48 | * @dev Adds two numbers, reverts on overflow. 49 | */ 50 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 51 | uint256 c = a + b; 52 | require(c >= a); 53 | 54 | return c; 55 | } 56 | 57 | /** 58 | * @dev Divides two numbers and returns the remainder (unsigned integer modulo), 59 | * reverts when dividing by zero. 60 | */ 61 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 62 | require(b != 0); 63 | return a % b; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/TokenShield.sol: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | @notice © Copyright 2018 EYGS LLP and/or other members of the global Ernst & Young/EY network; pat. pending. 4 | 5 | @title TokenShield V1 6 | 7 | Contract to enable the management of hidden non fungible toke transactions. Currently it can 8 | only cope with tokens that are 8 bytes long. A normal tokenhash is 32 bytes long 9 | TODO - need to cope with the other 24 bytes and deeper Merkle tree. 10 | */ 11 | pragma solidity ^0.4.19; 12 | contract Verifier{ 13 | function verifyTx( 14 | uint[2], 15 | uint[2], 16 | uint[2][2], 17 | uint[2], 18 | uint[2], 19 | uint[2], 20 | uint[2], 21 | uint[2], 22 | address 23 | ) public pure returns (bool){} 24 | function getInputBits(uint, address) public view returns(bytes8){} 25 | } 26 | 27 | contract TokenShield{ 28 | address public owner; 29 | bytes8[merkleWidth] private ns; //store spent token nullifiers 30 | bytes8[merkleWidth] private ds; //store the double-spend prevention hashes 31 | uint constant merkleWidth = 256; 32 | uint constant merkleDepth = 9; 33 | uint constant lastRow = merkleDepth-1; 34 | bytes8[merkleWidth] private zs; //array holding the tokens. Basically the bottom row of the merkle tree 35 | uint private zCount; //remember the number of tokens we hold 36 | uint private nCount; //remember the number of tokens we spent 37 | bytes8[] private roots; //holds each root we've calculated so that we can pull the one relevant to the prover 38 | uint public currentRootIndex; //holds the index for the current root so that the 39 | //prover can provide it later and this contract can look up the relevant root 40 | Verifier mv; //the verification smart contract that the mint function uses 41 | Verifier tv; //the verification smart contract that the transfer function uses 42 | Verifier jv; //the verification smart contract that the join function uses 43 | Verifier sv; //the verification smart contract that the split function uses 44 | //uint i; //just a loop counter, should be local but moved here to preserve stack space 45 | struct Proof { //recast this as a struct because otherwise, as a set of local variable, it takes too much stack space 46 | uint[2] a; 47 | uint[2] a_p; 48 | uint[2][2] b; 49 | uint[2] b_p; 50 | uint[2] c; 51 | uint[2] c_p; 52 | uint[2] h; 53 | uint[2] k; 54 | } 55 | 56 | mapping(address => Proof) private proofs; 57 | 58 | constructor(address mintVerifier, address transferVerifier, address joinVerifier, address splitVerifier) public { 59 | //TODO - you can get a way with a single, generic verifier. 60 | owner = msg.sender; 61 | mv = Verifier(mintVerifier); 62 | tv = Verifier(transferVerifier); 63 | jv = Verifier(joinVerifier); 64 | sv = Verifier(splitVerifier); 65 | } 66 | 67 | 68 | //only owner modifier 69 | modifier onlyOwner () { 70 | require(msg.sender == owner); 71 | _; 72 | } 73 | 74 | /** 75 | self destruct added by westlad 76 | */ 77 | function close() public onlyOwner { 78 | selfdestruct(owner); 79 | } 80 | 81 | 82 | function getMintVerifier() public view returns(address){ 83 | return address(mv); 84 | } 85 | 86 | function getTransferVerifier() public view returns(address){ 87 | return address(tv); 88 | } 89 | 90 | function getJoinVerifier() public view returns(address){ 91 | return address(jv); 92 | } 93 | 94 | function getSplitVerifier() public view returns(address){ 95 | return address(sv); 96 | } 97 | 98 | /** 99 | The mint function accepts a Preventifier, H(A), where A is the assetHash; a 100 | Z token and a proof that both the token and the Preventifier contain A. 101 | It's done as an array because the stack on EVM is too small to hold all the locals otherwise. 102 | For the same reason, the proof is set up by calling setProofParams first. 103 | */ 104 | function mint() public { 105 | //first, verify the proof 106 | bool result = mv.verifyTx( 107 | proofs[msg.sender].a, 108 | proofs[msg.sender].a_p, 109 | proofs[msg.sender].b, 110 | proofs[msg.sender].b_p, 111 | proofs[msg.sender].c, 112 | proofs[msg.sender].c_p, 113 | proofs[msg.sender].h, 114 | proofs[msg.sender].k, 115 | msg.sender); 116 | 117 | require(result); //the proof must check out. 118 | bytes8 d = mv.getInputBits(0, msg.sender); //recover the input params from MintVerifier 119 | bytes8 z = mv.getInputBits(64, msg.sender); 120 | for (uint i=0; i VerifyingKey) private vks; 170 | mapping(address => uint[]) private vectors; 171 | mapping(address => Pairing.G1Point) private vk_xs; 172 | 173 | function setKeyLength(uint l) public { 174 | vks[msg.sender].IC.length = l; 175 | vectors[msg.sender].length = l-1; 176 | } 177 | 178 | function loadVerifyingKeyPreamble( 179 | uint[2][2] A, 180 | uint[2] B, 181 | uint[2][2] C, 182 | uint[2][2] gamma, 183 | uint[2] gammaBeta1, 184 | uint[2][2] gammaBeta2, 185 | uint[2][2] Z 186 | ) public { 187 | /** 188 | @notice © Copyright 2018 EYGS LLP and/or other members of the global Ernst & Young/EY network; pat. pending. 189 | */ 190 | vks[msg.sender].A = Pairing.G2Point([A[0][0],A[0][1]],[A[1][0],A[1][1]]); 191 | vks[msg.sender].B = Pairing.G1Point(B[0],B[1]); 192 | vks[msg.sender].C = Pairing.G2Point([C[0][0],C[0][1]],[C[1][0],C[1][1]]); 193 | vks[msg.sender].gamma = Pairing.G2Point([gamma[0][0],gamma[0][1]],[gamma[1][0],gamma[1][1]]); 194 | vks[msg.sender].gammaBeta1 = Pairing.G1Point(gammaBeta1[0],gammaBeta1[1]); 195 | vks[msg.sender].gammaBeta2 = Pairing.G2Point([gammaBeta2[0][0],gammaBeta2[0][1]],[gammaBeta2[1][0],gammaBeta2[1][1]]); 196 | vks[msg.sender].Z = Pairing.G2Point([Z[0][0],Z[0][1]],[Z[1][0],Z[1][1]]); 197 | //this seems a good place to initialise the vk_x computation 198 | vk_xs[msg.sender] = Pairing.G1Point(0, 0); //initialise 199 | 200 | } 201 | 202 | function loadVerifyingKey(uint[2][] points, uint start) public{ 203 | /** 204 | @notice © Copyright 2018 EYGS LLP and/or other members of the global Ernst & Young/EY network; pat. pending. 205 | */ 206 | //vk_xs[addr].X =0; vk_x.Y=0; //reset the vk_x computation for next time 207 | for (uint i=0; i