├── cairo ├── contracts │ ├── main.json │ ├── main_abi.json │ ├── Core_address.json │ ├── main2_address.json │ ├── ERC721URIStorage.cairo │ ├── ERC721.cairo │ ├── Storage.cairo │ ├── utils.cairo │ └── Getters.cairo ├── hash │ ├── input.json │ └── circuit.cairo ├── encryption │ ├── input.json │ └── circuit.cairo └── utils │ ├── encrypt.cairo │ ├── mimc.cairo │ └── ec_math.cairo ├── deployments └── ropsten │ ├── Core.json │ └── Getters.json ├── public ├── dev │ ├── Pairing.json │ ├── DarkForestUtils.json │ ├── EncryptionVerifier.json │ └── DarkForestCore.json ├── cairo.json ├── favicon.png ├── contracts │ ├── prod │ │ ├── Pairing.json │ │ ├── DarkForestUtils.json │ │ ├── IFactRegistry.json │ │ ├── EncryptionVerifier.json │ │ ├── DarkForestCore.json │ │ └── Getters.json │ └── Getters.json ├── main2_address.json ├── circuits │ ├── keys │ │ ├── Hash.zkey │ │ ├── BlurImage.zkey │ │ └── Encryption.zkey │ ├── wasm │ │ ├── Hash.wasm │ │ ├── BlurImage.wasm │ │ ├── Encryption.wasm │ │ └── SharedKey.wasm │ └── verification_keys │ │ ├── SharedKey.json │ │ ├── Hash.json │ │ └── Encryption.json ├── fonts │ ├── Roboto-subset.woff2 │ ├── Roboto-Mono-Regular.woff2 │ ├── static │ │ ├── RobotoMono-Bold.ttf │ │ ├── RobotoMono-Italic.ttf │ │ ├── RobotoMono-Light.ttf │ │ ├── RobotoMono-Medium.ttf │ │ ├── RobotoMono-Thin.ttf │ │ ├── RobotoMono-Regular.ttf │ │ ├── RobotoMono-SemiBold.ttf │ │ ├── RobotoMono-BoldItalic.ttf │ │ ├── RobotoMono-ExtraLight.ttf │ │ ├── RobotoMono-LightItalic.ttf │ │ ├── RobotoMono-ThinItalic.ttf │ │ ├── RobotoMono-MediumItalic.ttf │ │ ├── RobotoMono-ExtraLightItalic.ttf │ │ └── RobotoMono-SemiBoldItalic.ttf │ ├── RobotoMono-VariableFont_wght.ttf │ ├── RobotoMono-Italic-VariableFont_wght.ttf │ └── README.txt ├── icons8-delete.svg ├── keys.json └── index.html ├── config ├── index.js └── config.js ├── src ├── types │ ├── snarkjs.d.ts │ ├── content.ts │ └── index.ts ├── index.tsx ├── utils │ ├── utils.ts │ ├── api.ts │ ├── parsers.ts │ ├── errors.ts │ ├── signature.ts │ └── prover.ts ├── app │ ├── OurThemeProvider.tsx │ ├── PropertyToggle.tsx │ ├── Page.tsx │ ├── App.tsx │ ├── NavigationBar.tsx │ └── Ipfs.tsx ├── components │ ├── Button.tsx │ ├── Spinner.tsx │ ├── TextArea.tsx │ ├── Date.tsx │ ├── Toggle.tsx │ ├── Modal.tsx │ ├── Resize.tsx │ ├── text.tsx │ └── TextInput.tsx ├── styles │ └── theme.ts └── pages │ ├── ChooseUser.tsx │ ├── Content.tsx │ ├── content │ ├── Hash.tsx │ └── Blur.tsx │ ├── Tokens.tsx │ └── NewToken.tsx ├── test └── spec.js ├── circuits ├── hash │ ├── input.json │ ├── public.json │ └── circuit.circom ├── dark-forest │ ├── input.json │ └── circuit.circom ├── blur-image │ ├── input.json │ └── circuit.circom ├── encryption │ ├── input.json │ ├── public.json │ └── circuit.circom ├── test │ ├── encryption.test.js │ ├── df.test.js │ ├── blurredImage.test.js │ └── hash.test.js └── utils │ ├── ecdh.circom │ └── encrypt.circom ├── artifacts ├── contracts │ ├── Core.sol │ │ ├── Core.dbg.json │ │ ├── DarkForestCore.dbg.json │ │ ├── IFactRegistry.dbg.json │ │ ├── IFactRegistry.json │ │ └── DarkForestCore.json │ ├── Getters.sol │ │ ├── ICore.dbg.json │ │ └── Getters.dbg.json │ ├── Pairing.sol │ │ ├── Pairing.dbg.json │ │ └── Pairing.json │ ├── Verifier.sol │ │ └── Verifier.dbg.json │ ├── DarkForestCore.sol │ │ ├── DarkForestCore.dbg.json │ │ └── DarkForestCore.json │ ├── ContractStorage.sol │ │ └── ContractStorage.dbg.json │ └── DarkForestUtils.sol │ │ ├── DarkForestUtils.dbg.json │ │ └── DarkForestUtils.json └── @openzeppelin │ └── contracts │ ├── access │ └── Ownable.sol │ │ ├── Ownable.dbg.json │ │ └── Ownable.json │ ├── utils │ ├── Address.sol │ │ ├── Address.dbg.json │ │ └── Address.json │ ├── Context.sol │ │ ├── Context.dbg.json │ │ └── Context.json │ ├── Counters.sol │ │ ├── Counters.dbg.json │ │ └── Counters.json │ ├── Strings.sol │ │ ├── Strings.dbg.json │ │ └── Strings.json │ ├── introspection │ │ ├── ERC165.sol │ │ │ ├── ERC165.dbg.json │ │ │ └── ERC165.json │ │ └── IERC165.sol │ │ │ ├── IERC165.dbg.json │ │ │ └── IERC165.json │ └── structs │ │ └── EnumerableSet.sol │ │ ├── EnumerableSet.dbg.json │ │ └── EnumerableSet.json │ ├── token │ └── ERC721 │ │ ├── ERC721.sol │ │ └── ERC721.dbg.json │ │ ├── IERC721.sol │ │ └── IERC721.dbg.json │ │ ├── IERC721Receiver.sol │ │ ├── IERC721Receiver.dbg.json │ │ └── IERC721Receiver.json │ │ └── extensions │ │ ├── ERC721URIStorage.sol │ │ └── ERC721URIStorage.dbg.json │ │ └── IERC721Metadata.sol │ │ └── IERC721Metadata.dbg.json │ └── proxy │ └── utils │ └── Initializable.sol │ ├── Initializable.dbg.json │ └── Initializable.json ├── jest.config.js ├── .gitignore ├── .env.example ├── scripts ├── cairoCompile.js ├── cairoDeploy.js ├── cairoCall.js ├── deploy.ts ├── utils.ts ├── buildConfig.ts ├── build_readme.js ├── solbuilder.ts └── builder.ts ├── contracts ├── DarkForestUtils.sol ├── DarkForestCore.sol ├── ContractStorage.sol └── Getters.sol ├── tsconfig.json ├── .github └── workflows │ └── config.yml ├── crypto ├── test.js ├── test.py ├── package.json ├── index.js └── babyjub.js ├── .eslintrc.json ├── app.py ├── hardhat.config.ts ├── webpack.config.js └── README.md /cairo/contracts/main.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cairo/contracts/main_abi.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /deployments/ropsten/Core.json: -------------------------------------------------------------------------------- 1 | {"address":"0x6d0ED701C55D33816EB205D9Cb1A9A69325FA8df"} -------------------------------------------------------------------------------- /deployments/ropsten/Getters.json: -------------------------------------------------------------------------------- 1 | {"address":"0xD210BF9FBB9E9aB9D155dD017c7982Dd07D54aB2"} -------------------------------------------------------------------------------- /public/dev/Pairing.json: -------------------------------------------------------------------------------- 1 | {"abi":[],"address":"0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB"} -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | const config = require('./config.json'); 2 | 3 | export default config; 4 | -------------------------------------------------------------------------------- /public/cairo.json: -------------------------------------------------------------------------------- 1 | {"address":"0x078b1a2be50394f490f14525a9f122ae749138ed8e57f1373f34f85d8ddb58f0"} -------------------------------------------------------------------------------- /public/dev/DarkForestUtils.json: -------------------------------------------------------------------------------- 1 | {"abi":[],"address":"0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"} -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/favicon.png -------------------------------------------------------------------------------- /public/contracts/prod/Pairing.json: -------------------------------------------------------------------------------- 1 | {"abi":[],"address":"0x64eFDCafFBCCe475C6C478e1FEF173de021Db3dA"} -------------------------------------------------------------------------------- /public/contracts/prod/DarkForestUtils.json: -------------------------------------------------------------------------------- 1 | {"abi":[],"address":"0x94CF1176D393F0515C6968E5a42EA77E1c6e2302"} -------------------------------------------------------------------------------- /cairo/contracts/Core_address.json: -------------------------------------------------------------------------------- 1 | {"address":"0x03735564c4c38aaa072c6a9a64401d185adb6aa2e38772920070960201b091c0"} -------------------------------------------------------------------------------- /public/main2_address.json: -------------------------------------------------------------------------------- 1 | { "address": "0x0310ce421df97989efddf111df5067eb123500a6b799b1a662c3b10433c2fecb" } 2 | -------------------------------------------------------------------------------- /src/types/snarkjs.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'snarkjs' { 2 | let _snarkjs: any; 3 | export = _snarkjs; 4 | } 5 | -------------------------------------------------------------------------------- /cairo/contracts/main2_address.json: -------------------------------------------------------------------------------- 1 | {"address":"0x078b1a2be50394f490f14525a9f122ae749138ed8e57f1373f34f85d8ddb58f0"} -------------------------------------------------------------------------------- /public/circuits/keys/Hash.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/circuits/keys/Hash.zkey -------------------------------------------------------------------------------- /public/circuits/wasm/Hash.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/circuits/wasm/Hash.wasm -------------------------------------------------------------------------------- /public/fonts/Roboto-subset.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/Roboto-subset.woff2 -------------------------------------------------------------------------------- /public/circuits/keys/BlurImage.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/circuits/keys/BlurImage.zkey -------------------------------------------------------------------------------- /public/circuits/keys/Encryption.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/circuits/keys/Encryption.zkey -------------------------------------------------------------------------------- /public/circuits/wasm/BlurImage.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/circuits/wasm/BlurImage.wasm -------------------------------------------------------------------------------- /public/circuits/wasm/Encryption.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/circuits/wasm/Encryption.wasm -------------------------------------------------------------------------------- /public/circuits/wasm/SharedKey.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/circuits/wasm/SharedKey.wasm -------------------------------------------------------------------------------- /public/fonts/Roboto-Mono-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/Roboto-Mono-Regular.woff2 -------------------------------------------------------------------------------- /test/spec.js: -------------------------------------------------------------------------------- 1 | describe('My Test Suite', () => { 2 | it('My Test Case', () => { 3 | expect(true).toEqual(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-Bold.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-Italic.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-Light.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-Medium.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-Thin.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-SemiBold.ttf -------------------------------------------------------------------------------- /circuits/hash/input.json: -------------------------------------------------------------------------------- 1 | {"preimage": "29047", "key": "863137912040348132018052427834338243447822049650932826602125816581803776895", "salt": "0"} 2 | -------------------------------------------------------------------------------- /public/fonts/RobotoMono-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/RobotoMono-VariableFont_wght.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-BoldItalic.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-ExtraLight.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-LightItalic.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-ThinItalic.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-MediumItalic.ttf -------------------------------------------------------------------------------- /artifacts/contracts/Core.sol/Core.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /public/fonts/static/RobotoMono-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/static/RobotoMono-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /artifacts/contracts/Getters.sol/ICore.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /public/fonts/RobotoMono-Italic-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nulven/EthDataMarketplace/HEAD/public/fonts/RobotoMono-Italic-VariableFont_wght.ttf -------------------------------------------------------------------------------- /artifacts/contracts/Core.sol/DarkForestCore.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/contracts/Core.sol/IFactRegistry.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/contracts/Getters.sol/Getters.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/contracts/Pairing.sol/Pairing.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/contracts/Verifier.sol/Verifier.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/contracts/DarkForestCore.sol/DarkForestCore.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/5e4333c8bbbb21d862c2d525f394e5b8.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/contracts/ContractStorage.sol/ContractStorage.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/contracts/DarkForestUtils.sol/DarkForestUtils.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../build-info/5e4333c8bbbb21d862c2d525f394e5b8.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/access/Ownable.sol/Ownable.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/Address.sol/Address.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/Counters.sol/Counters.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/Strings.sol/Strings.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/token/ERC721/ERC721.sol/ERC721.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/token/ERC721/IERC721.sol/IERC721.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/introspection/ERC165.sol/ERC165.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/introspection/IERC165.sol/IERC165.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/proxy/utils/Initializable.sol/Initializable.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/structs/EnumerableSet.sol/EnumerableSet.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol/IERC721Receiver.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: false, 3 | silent: false, 4 | transform: { 5 | '^.+\\.(js|jsx)$': 'babel-jest', 6 | }, 7 | roots: ['test/'], 8 | testTimeout: 100000000, 9 | }; 10 | -------------------------------------------------------------------------------- /cairo/hash/input.json: -------------------------------------------------------------------------------- 1 | {"preimage": 6841721, "key": 310189428346541121036033531041731175623531917337868002290492097685460605462, "hash": 1842142978386328626522018142232649629105807214573323389063844912639141478400, "salt": 0} 2 | -------------------------------------------------------------------------------- /circuits/dark-forest/input.json: -------------------------------------------------------------------------------- 1 | {"x":"1","y":"1","key":"19130618009890855922213564313454862638312549259625450262135402362633118556279","hash":"1829932551766186818808046918715427926359016229497829135073351261774593201598","salt":"100"} -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import 'regenerator-runtime/runtime'; 4 | 5 | import App from './app/App'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol/ERC721URIStorage.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol/IERC721Metadata.dbg.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-dbg-1", 3 | "buildInfo": "../../../../../../build-info/4d92d0003d1c0ee40921df925193f59a.json" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | dist 4 | 5 | cache 6 | deployments/localhost 7 | typechain 8 | compiledCircuits 9 | public/circuits 10 | public/contracts 11 | circuits/pots 12 | circuits/**/public.json 13 | config/config.json 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | beacon=69420753206121746f776e206f652deadbeef69420 2 | 3 | NODE_ENV='production' 4 | DARK_FOREST_CHECK=true 5 | NETWORK='ethereum' 6 | ETH_NETWORK='goerli' 7 | INFURA_ID= 8 | PRIVATE_KEY= 9 | 10 | STARKWARE_APP= 11 | IPFS_HOST= 12 | -------------------------------------------------------------------------------- /circuits/blur-image/input.json: -------------------------------------------------------------------------------- 1 | {"preimage":["1","0","1","0","1","1","0","1","0","1","1","0","1","0","1","0"],"key":"2359184173113251141614141781742266818757016097618414078533744063756205446350","blurred_image":["1","1","0","1","1","1","1","0","0","1","0","0","0","0","0","0"]} 2 | -------------------------------------------------------------------------------- /public/contracts/prod/IFactRegistry.json: -------------------------------------------------------------------------------- 1 | {"abi":[{"inputs":[{"internalType":"bytes32","name":"fact","type":"bytes32"}],"name":"isValid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}],"address":"0x1cE427292A0dA0313137A63Bb0f11423Bdb001A1"} -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import { eddsa } from 'circomlib'; 2 | const { prv2pub } = eddsa; 3 | 4 | 5 | export function generateKey() { 6 | const privateKey = Math.floor(Math.random()*1000000); 7 | const publicKey = prv2pub(privateKey.toString()); 8 | return { publicKey: publicKey, privateKey }; 9 | } 10 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/Context.sol/Context.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "Context", 4 | "sourceName": "@openzeppelin/contracts/utils/Context.sol", 5 | "abi": [], 6 | "bytecode": "0x", 7 | "deployedBytecode": "0x", 8 | "linkReferences": {}, 9 | "deployedLinkReferences": {} 10 | } 11 | -------------------------------------------------------------------------------- /circuits/hash/public.json: -------------------------------------------------------------------------------- 1 | [ 2 | "6331698529128173312328724742553669832712458337385102597780718268796728386482", 3 | "15854378775706544503278420080651022665425062488776096820298013246903470039624", 4 | "3705634327351415498095981027258195433370633503803501871539062401300116082061", 5 | "15854378775706544503278420080651022665425062488776096820298013246903470039624", 6 | "0" 7 | ] -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/proxy/utils/Initializable.sol/Initializable.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "Initializable", 4 | "sourceName": "@openzeppelin/contracts/proxy/utils/Initializable.sol", 5 | "abi": [], 6 | "bytecode": "0x", 7 | "deployedBytecode": "0x", 8 | "linkReferences": {}, 9 | "deployedLinkReferences": {} 10 | } 11 | -------------------------------------------------------------------------------- /public/icons8-delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /circuits/encryption/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "seller_private_key":"6050663213771722621313293647329553549388461959418068286695329582459496967523", 3 | "key":"8729111262329839830241629585398569384244836146053820612763916981345632638716", 4 | "buyer_public_key":["16980282661484878115671214823287432953665344253059199523584632944160508460651","6266136043108930971487815715909907304874922097403409717197353895245287358351"]} 5 | -------------------------------------------------------------------------------- /src/app/OurThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { selfTheme } from '../styles/theme'; 3 | import { ThemeProvider } from 'styled-components'; 4 | 5 | type Props = { 6 | theme?: Object, 7 | children?: any, 8 | }; 9 | 10 | const OurThemeProvider = ({ theme = selfTheme, children }: Props) => ( 11 | {children} 12 | ); 13 | 14 | export default OurThemeProvider; 15 | -------------------------------------------------------------------------------- /circuits/encryption/public.json: -------------------------------------------------------------------------------- 1 | [ 2 | "12231577831664109427884491305237465844870033304822640483867883774744043015449", 3 | "12231577831664109427884491305237465844870033304822640483867883774744043015449", 4 | "2157403240208156454308356452700593366942236299976858890111358881414927320818", 5 | "16980282661484878115671214823287432953665344253059199523584632944160508460651", 6 | "6266136043108930971487815715909907304874922097403409717197353895245287358351" 7 | ] -------------------------------------------------------------------------------- /scripts/cairoCompile.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | 3 | const circuitName = process.argv[2]; 4 | 5 | 6 | execSync(`cairo-compile ./cairo/${circuitName}/circuit.cairo --output ./cairo/${circuitName}/output.json`, { 7 | stdio: 'inherit', 8 | }); 9 | execSync(`cairo-run \ --program=./cairo/${circuitName}/output.json --print_output \ --layout=all --program_input=./cairo/${circuitName}/input.json`, { 10 | stdio: 'inherit', 11 | }); 12 | -------------------------------------------------------------------------------- /public/dev/EncryptionVerifier.json: -------------------------------------------------------------------------------- 1 | {"abi":[{"inputs":[{"internalType":"uint256[2]","name":"a","type":"uint256[2]"},{"internalType":"uint256[2][2]","name":"b","type":"uint256[2][2]"},{"internalType":"uint256[2]","name":"c","type":"uint256[2]"},{"internalType":"uint256[5]","name":"input","type":"uint256[5]"}],"name":"verifyProof","outputs":[{"internalType":"bool","name":"r","type":"bool"}],"stateMutability":"view","type":"function"}],"address":"0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690"} -------------------------------------------------------------------------------- /cairo/encryption/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "key":188190603173226452571209614695, 3 | "private_key": 452315666663287642995187579731270268772761801114665528803054973637467813437, 4 | "hash": 961326435832323455569339044438442358059148004349324307818006513300038135549, 5 | "public_key":[ 6 | 2349423674861681350081252861006308915679459427386583835881580551903189209824, 7 | 600605024227393229893796219070733899582636276087691818644927186376150058074 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /public/contracts/prod/EncryptionVerifier.json: -------------------------------------------------------------------------------- 1 | {"abi":[{"inputs":[{"internalType":"uint256[2]","name":"a","type":"uint256[2]"},{"internalType":"uint256[2][2]","name":"b","type":"uint256[2][2]"},{"internalType":"uint256[2]","name":"c","type":"uint256[2]"},{"internalType":"uint256[5]","name":"input","type":"uint256[5]"}],"name":"verifyProof","outputs":[{"internalType":"bool","name":"r","type":"bool"}],"stateMutability":"view","type":"function"}],"address":"0xC18ce1889e9f68d4eb77a1aCDC6ed9b2b3B4e726"} -------------------------------------------------------------------------------- /public/keys.json: -------------------------------------------------------------------------------- 1 | {"key1": {"priv_key": 2621838962916445197872527623277104754506935728346448963222041947773872800877, "pub_key": [1189160554838062097962858633664725757945894819485155525299130256407169751231, 142963258474582096651275112205309753001937346823816204038395665243523575827]}, "key2": {"priv_key": 2973705177717982310042568628434942162446394144671186307436975577552595979581, "pub_key": [966946800390168890985276145999481310047700216190650127572317484086078517193, 1980286241577643615848147569966616802818827878816275753535351630041743387241]}} -------------------------------------------------------------------------------- /circuits/test/encryption.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { Keypair } = require('maci-domainobjs'); 3 | 4 | const key1 = new Keypair(); 5 | const key2 = new Keypair(); 6 | const sharedKey = Keypair.genEcdhSharedKey(key1.privKey, key2.pubKey); 7 | 8 | const input = { 9 | seller_private_key: key1.privKey.asCircuitInputs(), 10 | key: sharedKey.toString(), 11 | buyer_public_key: key2.pubKey.asCircuitInputs(), 12 | }; 13 | 14 | fs.writeFile( 15 | './circuits/encryption/input.json', 16 | JSON.stringify(input), 17 | () => {}, 18 | ); 19 | -------------------------------------------------------------------------------- /contracts/DarkForestUtils.sol: -------------------------------------------------------------------------------- 1 | library DarkForestUtils { 2 | struct Planet { 3 | address owner; 4 | bool isHomePlanet; 5 | } 6 | 7 | struct SnarkConstants { 8 | uint256 PLANETHASH_KEY; 9 | } 10 | 11 | struct GameStorage { 12 | mapping(uint256 => Planet) planets; 13 | SnarkConstants snarkConstants; 14 | } 15 | 16 | function getGameStorage() public pure returns (GameStorage storage ret) { 17 | bytes32 position = bytes32(uint256(1)); 18 | assembly { 19 | ret.slot := position 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/PropertyToggle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Toggle from '../components/Toggle'; 4 | import { ContentProperties } from '../types'; 5 | 6 | type PropertyToggleProps = { 7 | property: string; 8 | setProperty: (value: ContentProperties) => void; 9 | }; 10 | 11 | const PropertyToggle = (props: PropertyToggleProps) => { 12 | 13 | return ( 14 | 19 | ); 20 | }; 21 | 22 | export default PropertyToggle; 23 | -------------------------------------------------------------------------------- /artifacts/contracts/Pairing.sol/Pairing.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "Pairing", 4 | "sourceName": "contracts/Pairing.sol", 5 | "abi": [], 6 | "bytecode": "0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 7 | "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 8 | "linkReferences": {}, 9 | "deployedLinkReferences": {} 10 | } 11 | -------------------------------------------------------------------------------- /src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Button = styled.button` 4 | font-family: ${props => props.theme.fontFamily}; 5 | height: 40px; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | padding: 0; 10 | border-radius: 5px; 11 | border-width: 1px; 12 | border-style: solid; 13 | font-variation-settings: 'wght' 500; 14 | cursor: pointer; 15 | background-color: ${props => props.theme.color.primary}; 16 | :hover { 17 | background-color: ${props => props.theme.color.secondary}; 18 | } 19 | `; 20 | -------------------------------------------------------------------------------- /circuits/test/df.test.js: -------------------------------------------------------------------------------- 1 | const { mimcsponge } = require('circomlib'); 2 | const fs = require('fs'); 3 | const { Keypair } = require('maci-domainobjs'); 4 | 5 | const x = '1'; 6 | const y = '1'; 7 | 8 | const key1 = new Keypair(); 9 | const key2 = new Keypair(); 10 | 11 | const sharedKey = Keypair.genEcdhSharedKey(key1.privKey, key2.pubKey); 12 | const hash = mimcsponge.multiHash([x, y], BigInt(100), 1); 13 | 14 | const input = { 15 | x, 16 | y, 17 | key: sharedKey.toString(), 18 | hash: hash.toString(), 19 | salt: '100', 20 | }; 21 | 22 | fs.writeFile('./circuits/df/input.json', JSON.stringify(input), () => {}); 23 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/Address.sol/Address.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "Address", 4 | "sourceName": "@openzeppelin/contracts/utils/Address.sol", 5 | "abi": [], 6 | "bytecode": "0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 7 | "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 8 | "linkReferences": {}, 9 | "deployedLinkReferences": {} 10 | } 11 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/Counters.sol/Counters.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "Counters", 4 | "sourceName": "@openzeppelin/contracts/utils/Counters.sol", 5 | "abi": [], 6 | "bytecode": "0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 7 | "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 8 | "linkReferences": {}, 9 | "deployedLinkReferences": {} 10 | } 11 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/Strings.sol/Strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "Strings", 4 | "sourceName": "@openzeppelin/contracts/utils/Strings.sol", 5 | "abi": [], 6 | "bytecode": "0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 7 | "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 8 | "linkReferences": {}, 9 | "deployedLinkReferences": {} 10 | } 11 | -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const env = process.env.NODE_ENV; 3 | 4 | const defaultConfig = { 5 | enableDarkForestCheck: process.env.DARK_FOREST_CHECK === 'true', 6 | ipfsHost: process.env.IPFS_HOST, 7 | chain: process.env.NETWORK, 8 | starkwareApp: process.env.STARKWARE_APP, 9 | infuraId: process.env.INFURA_ID, 10 | }; 11 | 12 | const config = { 13 | development: { 14 | env: 'development', 15 | ...defaultConfig, 16 | }, 17 | production: { 18 | env: 'production', 19 | ...defaultConfig, 20 | }, 21 | }; 22 | const export_config = config[env]; 23 | 24 | module.exports = export_config; 25 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/structs/EnumerableSet.sol/EnumerableSet.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "EnumerableSet", 4 | "sourceName": "@openzeppelin/contracts/utils/structs/EnumerableSet.sol", 5 | "abi": [], 6 | "bytecode": "0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 7 | "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000803000a", 8 | "linkReferences": {}, 9 | "deployedLinkReferences": {} 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "outDir": "./dist/", 5 | "esModuleInterop": true, 6 | "module": "commonjs", 7 | "target": "es6", 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "allowSyntheticDefaultImports": true, 11 | "sourceMap": true, 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "noEmit": false, 15 | "noImplicitThis": false, 16 | "types": ["node"] 17 | }, 18 | "include": [ 19 | "*.ts", 20 | "**/*.ts" 21 | ], 22 | "exclude": [ 23 | "node_modules" 24 | ], 25 | "plugins": [ 26 | { 27 | "transform": "@zerollup/ts-transform-paths" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /artifacts/contracts/Core.sol/IFactRegistry.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "IFactRegistry", 4 | "sourceName": "contracts/Core.sol", 5 | "abi": [ 6 | { 7 | "inputs": [ 8 | { 9 | "internalType": "bytes32", 10 | "name": "fact", 11 | "type": "bytes32" 12 | } 13 | ], 14 | "name": "isValid", 15 | "outputs": [ 16 | { 17 | "internalType": "bool", 18 | "name": "", 19 | "type": "bool" 20 | } 21 | ], 22 | "stateMutability": "view", 23 | "type": "function" 24 | } 25 | ], 26 | "bytecode": "0x", 27 | "deployedBytecode": "0x", 28 | "linkReferences": {}, 29 | "deployedLinkReferences": {} 30 | } 31 | -------------------------------------------------------------------------------- /circuits/utils/ecdh.circom: -------------------------------------------------------------------------------- 1 | include "../../node_modules/circomlib/circuits/bitify.circom"; 2 | include "../../node_modules/circomlib/circuits/escalarmulany.circom"; 3 | 4 | 5 | template Ecdh() { 6 | // Note: private key 7 | // Needs to be hashed, and then pruned before 8 | // supplying it to the circuit 9 | signal private input private_key; 10 | signal input public_key[2]; 11 | 12 | signal output shared_key; 13 | 14 | var n = 253 15 | 16 | component privBits = Num2Bits(n); 17 | privBits.in <== private_key; 18 | 19 | component mulFix = EscalarMulAny(n); 20 | mulFix.p[0] <== public_key[0]; 21 | mulFix.p[1] <== public_key[1]; 22 | 23 | for (var i = 0; i < n; i++) { 24 | mulFix.e[i] <== privBits.out[i]; 25 | } 26 | 27 | shared_key <== mulFix.out[0]; 28 | } 29 | -------------------------------------------------------------------------------- /artifacts/contracts/DarkForestUtils.sol/DarkForestUtils.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "DarkForestUtils", 4 | "sourceName": "contracts/DarkForestUtils.sol", 5 | "abi": [], 6 | "bytecode": "0x6059610038600b82828239805160001a607314602b57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063630cf433146038575b600080fd5b600160405190815260200160405180910390f3fea164736f6c6343000803000a", 7 | "deployedBytecode": "0x730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063630cf433146038575b600080fd5b600160405190815260200160405180910390f3fea164736f6c6343000803000a", 8 | "linkReferences": {}, 9 | "deployedLinkReferences": {} 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/config.yml: -------------------------------------------------------------------------------- 1 | name: Build webpack 2 | on: [push] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions/setup-node@v1 10 | - run: echo NODE_ENV=production >> .env 11 | - run: echo ETH_NETWORK='goerli' >> .env 12 | - run: echo NETWORK='ethereum' >> .env 13 | - run: echo STARKWARE_APP='https://fossairdrop.com' >> .env 14 | - run: echo IPFS_HOST='fossairdrop.com' >> .env 15 | - run: echo INFURA_ID=secrets.INFURA_ID >> .env 16 | - run: npm install 17 | - run: npm run build 18 | - run: echo privatenfts.xyz >> ./dist/CNAME 19 | - uses: peaceiris/actions-gh-pages@v3 20 | with: 21 | deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} 22 | publish_dir: ./dist 23 | -------------------------------------------------------------------------------- /scripts/cairoDeploy.js: -------------------------------------------------------------------------------- 1 | const { execSync, exec } = require('child_process'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | const contractName = process.argv[2]; 6 | 7 | const cwd = process.cwd(); 8 | const directory = path.resolve(`${cwd}/cairo/contracts`); 9 | 10 | execSync(`starknet-compile ./cairo/contracts/${contractName}.cairo \ --output ./cairo/contracts/${contractName}.json \ --abi ./cairo/contracts/${contractName}_abi.json`, { 11 | stdio: 'inherit', 12 | }); 13 | exec(`starknet deploy --contract ./cairo/contracts/${contractName}.json`, (error, stdout, stderr) => { 14 | if (error) { 15 | console.log(error); 16 | } 17 | const address = stdout.match('(?<=Contract address: ).*')[0]; 18 | fs.writeFileSync(`${directory}/${contractName}_address.json`, JSON.stringify({ address })); 19 | }); 20 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/introspection/ERC165.sol/ERC165.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "ERC165", 4 | "sourceName": "@openzeppelin/contracts/utils/introspection/ERC165.sol", 5 | "abi": [ 6 | { 7 | "inputs": [ 8 | { 9 | "internalType": "bytes4", 10 | "name": "interfaceId", 11 | "type": "bytes4" 12 | } 13 | ], 14 | "name": "supportsInterface", 15 | "outputs": [ 16 | { 17 | "internalType": "bool", 18 | "name": "", 19 | "type": "bool" 20 | } 21 | ], 22 | "stateMutability": "view", 23 | "type": "function" 24 | } 25 | ], 26 | "bytecode": "0x", 27 | "deployedBytecode": "0x", 28 | "linkReferences": {}, 29 | "deployedLinkReferences": {} 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Spinner.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Loader from 'react-loader-spinner'; 4 | import { Large } from '../components/text'; 5 | 6 | 7 | const SpinnerWrapper = styled.div` 8 | display: flex; 9 | width: 40%; 10 | flex: 1; 11 | margin-left: 30%; 12 | margin-right: 30%; 13 | margin-top: 20%; 14 | flex-direction: column; 15 | align-items: center; 16 | `; 17 | 18 | type SpinnerProps = { 19 | loadingMessage: string; 20 | } 21 | 22 | const Title = styled(Large)` 23 | margin-top: 30px; 24 | `; 25 | 26 | const Spinner = (props: SpinnerProps) => { 27 | return ( 28 | 29 | 30 | {props.loadingMessage} 31 | 32 | ); 33 | }; 34 | 35 | 36 | export default Spinner; 37 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/utils/introspection/IERC165.sol/IERC165.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "IERC165", 4 | "sourceName": "@openzeppelin/contracts/utils/introspection/IERC165.sol", 5 | "abi": [ 6 | { 7 | "inputs": [ 8 | { 9 | "internalType": "bytes4", 10 | "name": "interfaceId", 11 | "type": "bytes4" 12 | } 13 | ], 14 | "name": "supportsInterface", 15 | "outputs": [ 16 | { 17 | "internalType": "bool", 18 | "name": "", 19 | "type": "bool" 20 | } 21 | ], 22 | "stateMutability": "view", 23 | "type": "function" 24 | } 25 | ], 26 | "bytecode": "0x", 27 | "deployedBytecode": "0x", 28 | "linkReferences": {}, 29 | "deployedLinkReferences": {} 30 | } 31 | -------------------------------------------------------------------------------- /crypto/test.js: -------------------------------------------------------------------------------- 1 | const BN = require('bn.js'); 2 | const hash = require('hash.js'); 3 | const { curves: eCurves, ec: EllipticCurve } = require('elliptic'); 4 | const assert = require('assert'); 5 | const { constantPoints, pedersen } = require('./signature.js'); 6 | 7 | const LOW_PART_BITS = 248 8 | const LOW_PART_MASK = 2**248-1 9 | const N_ELEMENT_BIT_HASH = 250; 10 | function processSingleElement(element, p1, p2) { 11 | const highNibble = element >> LOW_PART_BITS; 12 | console.log(p1); 13 | const lowPart = element & LOW_PART_MASK; 14 | return lowPart * p1 + highNibble * p2; 15 | } 16 | 17 | const shiftPoint = constantPoints[0]; 18 | const P_0 = constantPoints[2] 19 | const P_1 = constantPoints[2 + LOW_PART_BITS] 20 | const P_2 = constantPoints[2 + N_ELEMENT_BIT_HASH] 21 | const P_3 = constantPoints[2 + N_ELEMENT_BIT_HASH + LOW_PART_BITS] 22 | 23 | console.log(BigInt(`0x${pedersen([BigInt(1), BigInt(2)])}`)); 24 | -------------------------------------------------------------------------------- /public/contracts/prod/DarkForestCore.json: -------------------------------------------------------------------------------- 1 | {"abi":[{"inputs":[{"internalType":"uint256","name":"key","type":"uint256"}],"name":"planets","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"bool","name":"isHomePlanet","type":"bool"}],"internalType":"struct DarkForestCore.Planet","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"s","outputs":[{"components":[{"internalType":"uint256","name":"PLANETHASH_KEY","type":"uint256"}],"internalType":"struct DarkForestCore.SnarkConstants","name":"snarkConstants","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"snarkConstants","outputs":[{"components":[{"internalType":"uint256","name":"PLANETHASH_KEY","type":"uint256"}],"internalType":"struct DarkForestCore.SnarkConstants","name":"","type":"tuple"}],"stateMutability":"view","type":"function"}],"address":"0x201BECFfca76f54E3366EF7589FB6008E3F3B281"} -------------------------------------------------------------------------------- /src/types/content.ts: -------------------------------------------------------------------------------- 1 | import { Snark, Stark, ZKTypes } from '../types'; 2 | 3 | interface InputProps { 4 | preimage: string; 5 | setPreimage: (value: string) => void; 6 | } 7 | 8 | interface DisplayProps { 9 | property: any; 10 | message?: any; 11 | } 12 | 13 | interface ContentSkeleton { 14 | display: (props: DisplayProps) => JSX.Element; 15 | input: (props: InputProps) => JSX.Element; 16 | list: (props: DisplayProps) => JSX.Element; 17 | decrypt: (zk: ZKTypes, ciphertext: any, key: BigInt) => any; 18 | computeProperty: (preimage: any, key?: BigInt) => any[]; 19 | prover: Record Promise>; 20 | verifier: Record Promise>; 21 | assertProofInputs: (args: any[]) => void; 22 | assertContent: (content: any) => void; 23 | assertMessage: (message: any) => void; 24 | } 25 | 26 | export { 27 | InputProps, 28 | ContentSkeleton, 29 | }; 30 | -------------------------------------------------------------------------------- /circuits/test/blurredImage.test.js: -------------------------------------------------------------------------------- 1 | function dec2bin(dec) { 2 | return dec.toString(2); 3 | } 4 | 5 | function blurImage(preImage, key) { 6 | const keyBits = dec2bin(key); 7 | const blurredImage = preImage.map((bit, index) => { 8 | return bit^keyBits[keyBits.length-1-index]; 9 | }); 10 | return blurredImage; 11 | } 12 | 13 | const fs = require('fs'); 14 | const { Keypair } = require('maci-domainobjs'); 15 | 16 | const preimage = [1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0]; 17 | 18 | const key1 = new Keypair(); 19 | const key2 = new Keypair(); 20 | 21 | const sharedKey = Keypair.genEcdhSharedKey(key1.privKey, key2.pubKey); 22 | 23 | const input = { 24 | preimage: preimage.map(_ => _.toString()), 25 | key: sharedKey.toString(), 26 | blurred_image: blurImage(preimage, sharedKey).map(_ => _.toString()), 27 | }; 28 | 29 | fs.writeFile( 30 | './circuits/blur-image/input.json', 31 | JSON.stringify(input), 32 | () => {}, 33 | ); 34 | -------------------------------------------------------------------------------- /circuits/blur-image/circuit.circom: -------------------------------------------------------------------------------- 1 | include "../../node_modules/circomlib/circuits/bitify.circom" 2 | include "../../node_modules/circomlib/circuits/mimc.circom" 3 | 4 | template Main() { 5 | var N = 16; 6 | signal private input preimage[N]; 7 | signal private input key; 8 | signal input blurred_image[N]; 9 | signal output hash; 10 | signal output computed_image[N]; 11 | 12 | // hash of key 13 | component mimc = MultiMiMC7(1, 91); 14 | mimc.in[0] <== key; 15 | mimc.k <== 0; 16 | hash <== mimc.out; 17 | 18 | // proof of property & encryption of message (blur) 19 | component num2bits = Num2Bits(256); 20 | num2bits.in <== key; 21 | signal key_bits[N]; 22 | for (var i=0; i (hash: felt): 11 | let (hash) = hash2{hash_ptr=pedersen_ptr}(x, y) 12 | return (hash) 13 | end 14 | 15 | func Encrypt{pedersen_ptr: HashBuiltin*}(plaintext: felt, shared_key: felt) -> (out: Ciphertext): 16 | let (hash1) = get_hash_pedersen(plaintext, 0) 17 | 18 | let (hash2) = get_hash_pedersen(shared_key, hash1) 19 | 20 | let b = plaintext + hash2 21 | let out = Ciphertext(a=hash1, b=b) 22 | return (out) 23 | end 24 | 25 | func Decrypt{pedersen_ptr: HashBuiltin*}(ciphertext: Ciphertext, shared_key: felt) -> (out: felt): 26 | let (hash) = get_hash_pedersen(shared_key, ciphertext.a) 27 | let out = ciphertext.b - hash 28 | return (out) 29 | end 30 | -------------------------------------------------------------------------------- /contracts/DarkForestCore.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.7.6; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./DarkForestUtils.sol"; 5 | 6 | contract DarkForestCore { 7 | DarkForestUtils.GameStorage public s; 8 | 9 | constructor() public { 10 | DarkForestUtils.Planet memory planet = DarkForestUtils.Planet({ 11 | owner: msg.sender, 12 | isHomePlanet: true 13 | }); 14 | 15 | uint256 location = 5228530872000388647816463285860870411975878209802611613611963004792914824074; 16 | s.planets[location] = planet; 17 | 18 | s.snarkConstants = DarkForestUtils.SnarkConstants({ 19 | PLANETHASH_KEY: 2149252641268674884343858554091843927095993520400352350070838229268101760471 20 | }); 21 | } 22 | 23 | function planets(uint256 key) public view returns (DarkForestUtils.Planet memory) { 24 | return s.planets[key]; 25 | } 26 | 27 | function snarkConstants() public view returns (DarkForestUtils.SnarkConstants memory) { 28 | return s.snarkConstants; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /crypto/test.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from starkware.crypto.signature.math_utils import ec_add, ec_mult 4 | from starkware.crypto.signature.signature import get_random_private_key, private_key_to_ec_point_on_stark_curve, FIELD_PRIME 5 | from starkware.crypto.signature.fast_pedersen_hash import pedersen_hash 6 | 7 | 8 | def genKeypair(): 9 | priv_key = get_random_private_key() 10 | public_key = private_key_to_ec_point_on_stark_curve(priv_key) 11 | 12 | keypair = { 'priv_key': priv_key, 'pub_key': public_key } 13 | return keypair 14 | 15 | def gen_shared_key(priv_key, pub_key): 16 | return ec_mult(priv_key, pub_key, 1, FIELD_PRIME)[0] 17 | 18 | 19 | def gen_keys(): 20 | key_pair1 = genKeypair() 21 | key_pair2 = genKeypair() 22 | data = { 'key1': key_pair1, 'key2': key_pair2 } 23 | with open('./public/keys.json', 'w') as f: 24 | json.dump(data, f) 25 | 26 | 27 | print(private_key_to_ec_point_on_stark_curve(1463287801087371787425637237490104066531018057258318019831177542025430958080)) 28 | -------------------------------------------------------------------------------- /circuits/hash/circuit.circom: -------------------------------------------------------------------------------- 1 | include "../../node_modules/circomlib/circuits/mimcsponge.circom" 2 | include "../../node_modules/circomlib/circuits/bitify.circom" 3 | 4 | include "../utils/encrypt.circom"; 5 | include "../utils/ecdh.circom"; 6 | 7 | template Main() { 8 | signal private input preimage; 9 | signal private input key; 10 | signal input salt; 11 | signal output key_hash; 12 | signal output ciphertext[2]; 13 | signal output hash; 14 | 15 | // hash of key 16 | component mimcKey = MultiMiMC7(1, 91); 17 | mimcKey.in[0] <== key; 18 | mimcKey.k <== 0; 19 | key_hash <== mimcKey.out; 20 | 21 | // proof of property (hash) 22 | component mimc = MultiMiMC7(1, 91); 23 | mimc.in[0] <== preimage; 24 | mimc.k <== salt; 25 | mimc.out ==> hash; 26 | 27 | // encryption of message 28 | component encrypt = Encrypt(); 29 | encrypt.plaintext <== preimage; 30 | encrypt.shared_key <== key; 31 | ciphertext[0] <== encrypt.out[0]; 32 | ciphertext[1] <== encrypt.out[1]; 33 | } 34 | 35 | component main = Main(); 36 | -------------------------------------------------------------------------------- /circuits/test/hash.test.js: -------------------------------------------------------------------------------- 1 | function stringToBits(string) { 2 | const buff = Buffer.from(string); 3 | let bitString = ''; 4 | buff.forEach(integer => { 5 | const bits = integer.toString(2).padStart(8, '0'); 6 | bitString += bits; 7 | }); 8 | return bitString; 9 | } 10 | 11 | function stringToNum(string) { 12 | const bitString = stringToBits(string); 13 | const number = BigInt(parseInt(bitString, 2)); 14 | return number; 15 | } 16 | 17 | const { mimc7 } = require('circomlib'); 18 | const fs = require('fs'); 19 | const { Keypair } = require('maci-domainobjs'); 20 | 21 | const preimage = 'hello'; 22 | const preimageNum = stringToNum(preimage); 23 | 24 | const key1 = new Keypair(); 25 | const key2 = new Keypair(); 26 | 27 | const sharedKey = Keypair.genEcdhSharedKey(key1.privKey, key2.pubKey); 28 | const hash = mimc7.multiHash([preimageNum], BigInt(100)); 29 | 30 | const input = { 31 | preimage: preimageNum.toString(), 32 | key: sharedKey.toString(), 33 | hash: hash.toString(), 34 | salt: '100', 35 | }; 36 | 37 | fs.writeFile('./circuits/hash/input.json', JSON.stringify(input), () => {}); 38 | -------------------------------------------------------------------------------- /circuits/dark-forest/circuit.circom: -------------------------------------------------------------------------------- 1 | include "../../node_modules/circomlib/circuits/mimcsponge.circom" 2 | include "../../node_modules/circomlib/circuits/bitify.circom" 3 | 4 | include "../utils/ecdh.circom"; 5 | include "../utils/encrypt.circom"; 6 | 7 | template Main() { 8 | signal private input x; 9 | signal private input y; 10 | signal private input key; 11 | signal input hash; 12 | signal input salt; 13 | signal output key_hash; 14 | signal output ciphertext[3]; 15 | 16 | // hash of key 17 | component mimcKey = MultiMiMC7(1, 91); 18 | mimcKey.in[0] <== key; 19 | mimcKey.k <== 0; 20 | key_hash <== mimcKey.out; 21 | 22 | // proof of property (hash) 23 | component mimc = MiMCSponge(2, 220, 1); 24 | mimc.ins[0] <== x; 25 | mimc.ins[1] <== y; 26 | mimc.k <== salt; 27 | mimc.outs[0] === hash; 28 | 29 | // encryption of message 30 | component encrypt = EncryptBits(2); 31 | encrypt.plaintext[0] <== x; 32 | encrypt.plaintext[1] <== y; 33 | encrypt.shared_key <== key; 34 | ciphertext[0] <== encrypt.out[0]; 35 | ciphertext[1] <== encrypt.out[1]; 36 | ciphertext[2] <== encrypt.out[2]; 37 | 38 | } 39 | 40 | component main = Main(); 41 | -------------------------------------------------------------------------------- /crypto/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starkware_crypto", 3 | "version": "0.1.4", 4 | "description": "Signatures, keys and Pedersen hash on STARK friendly elliptic curve", 5 | "main": "signature.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "lint": "eslint -f unix --ext=.js ./" 9 | }, 10 | "keywords": [ 11 | "stark", 12 | "signature", 13 | "EC", 14 | "Elliptic", 15 | "curve", 16 | "Cryptography" 17 | ], 18 | "files": [ 19 | "signature.js", 20 | "test/signature_test.js", 21 | "signature_example.js", 22 | "constant_points.json", 23 | "keys_precomputed.json", 24 | "signature_test_data.json" 25 | ], 26 | "dependencies": { 27 | "elliptic": "git://github.com/indutny/elliptic.git#v6.5.0", 28 | "bn.js": "^4.4.0", 29 | "brorand": "^1.0.1", 30 | "hash.js": "^1.0.0", 31 | "hmac-drbg": "^1.0.0", 32 | "inherits": "^2.0.1", 33 | "minimalistic-assert": "^1.0.0", 34 | "minimalistic-crypto-utils": "^1.0.0" 35 | }, 36 | "devDependencies": { 37 | "chai": "^4.2.0", 38 | "eslint": "^6.8.0", 39 | "mocha": "^7.1.0" 40 | }, 41 | "author": "StarkWare Industries Ltd.", 42 | "license": "Apache-2.0" 43 | } 44 | -------------------------------------------------------------------------------- /src/components/TextArea.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | type Props = { 5 | id?: string, 6 | placeholder: string, 7 | handleEnter?: (string) => void, 8 | onChange: (value: string) => void, 9 | value: string, 10 | style?: object, 11 | }; 12 | 13 | const TextAreaWrapper = styled.textarea` 14 | font-family: 'Roboto Variable'; 15 | padding: 8px; 16 | height: 40px; 17 | width: calc(100% - 16px); 18 | display: flex; 19 | border-radius: 5px; 20 | border-width: 1px; 21 | border-style: solid; 22 | border-color: ${props => props.theme.color.primary}; 23 | font-variation-settings: 'wght' 500; 24 | `; 25 | 26 | function TextArea (props: Props) { 27 | const detectEnter = (e: any) => { 28 | if (e.key === 'Enter' && props.handleEnter) { 29 | props.handleEnter(e.target.value); 30 | } 31 | }; 32 | 33 | return ( 34 | props.onChange(e.target.value)} 38 | onKeyPress={detectEnter} 39 | placeholder={props.placeholder} 40 | value={props.value} 41 | /> 42 | ); 43 | } 44 | 45 | export default TextArea; 46 | -------------------------------------------------------------------------------- /cairo/contracts/ERC721URIStorage.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.starknet.common.storage import Storage 4 | from starkware.cairo.common.cairo_builtins import HashBuiltin 5 | 6 | @storage_var 7 | func _tokenURIs(token: felt) -> (uri: (felt, felt)): 8 | end 9 | 10 | func _setTokenURI{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(token_id: felt, _token_uri1: felt, _token_uri2: felt): 11 | #require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); 12 | let _token_uri: (felt, felt) = (_token_uri1, _token_uri2) 13 | _tokenURIs.write(token_id, _token_uri) 14 | 15 | return () 16 | end 17 | 18 | func tokenURI{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(token_id: felt) -> (uri: (felt, felt)): 19 | #require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token"); 20 | 21 | let (_tokenURI) = _tokenURIs.read(token_id) 22 | return (uri=_tokenURI) 23 | #let (base) = _baseURI() 24 | 25 | #if (bytes(base).length == 0): 26 | #return _tokenURI 27 | #end 28 | 29 | #if (bytes(_tokenURI).length > 0): 30 | #return string(abi.encodePacked(base, _tokenURI)) 31 | #end 32 | 33 | #return tokenURI(token_id) 34 | end 35 | -------------------------------------------------------------------------------- /src/app/Page.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { Route } from 'react-router-dom'; 3 | 4 | import NavigationBar from './NavigationBar'; 5 | import OurThemeProvider from './OurThemeProvider'; 6 | import { selfTheme } from '../styles/theme'; 7 | 8 | interface Props { 9 | path: string; 10 | Subpage: any; 11 | navbar: boolean; 12 | signer: any; 13 | web3: any; 14 | } 15 | 16 | export default function Page(props: Props) { 17 | const { path, Subpage, navbar } = props; 18 | 19 | document.body.style.backgroundColor = selfTheme.color.light; 20 | 21 | useEffect(() => { 22 | if (document) { 23 | const title = 'Data Marketplace'; 24 | document.title = title; 25 | } 26 | }, [path]); 27 | 28 | return ( 29 | ( 32 | 33 | {navbar ? 34 | 40 | : null} 41 | 42 | 43 | )} 44 | /> 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol/IERC721Receiver.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "IERC721Receiver", 4 | "sourceName": "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol", 5 | "abi": [ 6 | { 7 | "inputs": [ 8 | { 9 | "internalType": "address", 10 | "name": "operator", 11 | "type": "address" 12 | }, 13 | { 14 | "internalType": "address", 15 | "name": "from", 16 | "type": "address" 17 | }, 18 | { 19 | "internalType": "uint256", 20 | "name": "tokenId", 21 | "type": "uint256" 22 | }, 23 | { 24 | "internalType": "bytes", 25 | "name": "data", 26 | "type": "bytes" 27 | } 28 | ], 29 | "name": "onERC721Received", 30 | "outputs": [ 31 | { 32 | "internalType": "bytes4", 33 | "name": "", 34 | "type": "bytes4" 35 | } 36 | ], 37 | "stateMutability": "nonpayable", 38 | "type": "function" 39 | } 40 | ], 41 | "bytecode": "0x", 42 | "deployedBytecode": "0x", 43 | "linkReferences": {}, 44 | "deployedLinkReferences": {} 45 | } 46 | -------------------------------------------------------------------------------- /circuits/encryption/circuit.circom: -------------------------------------------------------------------------------- 1 | include "../../node_modules/circomlib/circuits/mimcsponge.circom" 2 | include "../../node_modules/circomlib/circuits/bitify.circom" 3 | 4 | include "../utils/encrypt.circom"; 5 | include "../utils/ecdh.circom"; 6 | 7 | template Main() { 8 | signal private input key; 9 | signal private input seller_private_key; 10 | signal input buyer_public_key[2]; 11 | signal output hash; 12 | signal output out[2]; 13 | 14 | component mimc = MultiMiMC7(1, 91); 15 | mimc.in[0] <== key; 16 | mimc.k <== 0; 17 | mimc.out ==> hash; 18 | 19 | // encrypt preimage 20 | component ecdh = Ecdh(); 21 | 22 | ecdh.private_key <== seller_private_key; 23 | ecdh.public_key[0] <== buyer_public_key[0]; 24 | ecdh.public_key[1] <== buyer_public_key[1]; 25 | 26 | signal shared_key; 27 | shared_key <== ecdh.shared_key; 28 | 29 | component encrypt = Encrypt(); 30 | encrypt.plaintext <== key; 31 | encrypt.shared_key <== shared_key; 32 | out[0] <== encrypt.out[0]; 33 | out[1] <== encrypt.out[1]; 34 | 35 | /* 36 | component decrypt = Decrypt(); 37 | decrypt.message[0] <== out[0]; 38 | decrypt.message[1] <== out[1]; 39 | decrypt.shared_key <== shared_key; 40 | signal output m; 41 | m <== decrypt.out; 42 | */ 43 | } 44 | 45 | component main = Main(); 46 | -------------------------------------------------------------------------------- /src/components/Date.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | 5 | const DateWrapper = styled.div` 6 | display: block; 7 | color: ${props => props.theme.color.grey50}; 8 | `; 9 | 10 | type TimeBreakType = [number, string]; 11 | type TimeBreaksType = TimeBreakType[]; 12 | const TimeBreaks: TimeBreaksType = [ 13 | [1*7*24*60*60*1000, '1 week ago'], 14 | [6*24*60*60*1000, '6 days ago'], 15 | [5*24*60*60*1000, '5 days ago'], 16 | [4*24*60*60*1000, '4 days ago'], 17 | [3*24*60*60*1000, '3 days ago'], 18 | [2*24*60*60*1000, '2 days ago'], 19 | [1*24*60*60*1000, '1 day ago'], 20 | [2*60*60*1000, '2 hours ago'], 21 | [1*60*60*1000, '1 hour ago'], 22 | [6*60*1000, '5 minutes ago'], 23 | [5*60*1000, '4 minutes ago'], 24 | [4*60*1000, '3 minutes ago'], 25 | [3*60*1000, '2 minutes ago'], 26 | [2*60*1000, '1 minutes ago'], 27 | [1*60*1000, 'seconds ago'], 28 | ]; // breaks in milliseconds 29 | 30 | type DateProps = { 31 | timestamp: Date; 32 | }; 33 | 34 | const DateDiv = (props: DateProps) => { 35 | 36 | const now = new Date(); 37 | const elapsedTime = now.getTime() - props.timestamp.getTime(); 38 | var text = props.timestamp.toLocaleDateString(); 39 | TimeBreaks.forEach(([diff, value]) => { 40 | if (elapsedTime < diff) { 41 | text = value; 42 | } 43 | }); 44 | 45 | return ( 46 | {text} 47 | ); 48 | }; 49 | 50 | export default DateDiv; 51 | -------------------------------------------------------------------------------- /src/styles/theme.ts: -------------------------------------------------------------------------------- 1 | const unit = 8; 2 | 3 | const selfTheme = { 4 | spacing: (multiple: number) => (multiple * unit).toString() + 'px', 5 | fontFamily: 'Roboto Variable', 6 | color: { 7 | primary: '#3182ce', 8 | secondary: '#3182ce63', 9 | white: '#FFFFFF', 10 | darkText: '#091F43', 11 | grey90: '#545E6F', 12 | grey80: '#657287', 13 | grey70: '#738198', 14 | grey60: '#8894A8', 15 | grey50: '#A4AEBD', 16 | grey40: '#B9C1CE', 17 | grey30: '#D4DAE2', 18 | grey20: '#EEF0F5', 19 | grey10: '#F8F9FB', 20 | blue: '#0068FF', 21 | red: 'red', 22 | dark: 'rgb(20, 20, 20)', 23 | light: '#FFFFFF', 24 | }, 25 | text: { 26 | small: { size: '14px', lineHeight: '1.71' }, 27 | regular: { size: '16px', lineHeight: '1.50' }, 28 | medium: { size: '18px', lineHeight: '1.56' }, 29 | large: { size: '20px', lineHeight: '1.60' }, 30 | xlarge: { size: '32px', lineHeight: '1.80' }, 31 | }, 32 | borderRadii: { 33 | standard: '4px', 34 | curvy: '8px', 35 | circle: '50%', 36 | }, 37 | get buttonSizes() { 38 | return { 39 | small: { height: this.spacing(4), padding: this.spacing(2) }, 40 | medium: { height: this.spacing(5), padding: this.spacing(3) }, 41 | large: { height: this.spacing(6), padding: this.spacing(4) }, 42 | }; 43 | }, 44 | get border() { 45 | return ('1px solid ' + this.color.grey30); 46 | }, 47 | }; 48 | 49 | export { selfTheme }; 50 | -------------------------------------------------------------------------------- /src/components/Toggle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | 5 | const ToggleWrapper = styled.div` 6 | display: flex; 7 | flex-direction: row; 8 | align-items: center; 9 | height: 60px; 10 | background-color: white; 11 | margin-bottom: 10px; 12 | margin-left: auto; 13 | margin-right: auto; 14 | `; 15 | 16 | type ElementDivProps = { 17 | active: boolean; 18 | }; 19 | 20 | const ElementDiv = styled.div` 21 | display: block; 22 | box-sizing: border-box; 23 | width: 100px; 24 | height: 100%; 25 | text-align: center; 26 | padding: 20px 0px; 27 | background-color: ${props => (props.active ? 28 | props.theme.color.secondary : props.theme.color.grey30)}; 29 | :hover { 30 | background-color: ${props => props.theme.color.secondary}; 31 | } 32 | `; 33 | 34 | type ToggleProps = { 35 | element: string; 36 | elements: object; 37 | setElement: (value) => void; 38 | }; 39 | 40 | const Toggle = (props: ToggleProps) => { 41 | 42 | const setElement = (value) => () => { 43 | props.setElement(value); 44 | }; 45 | 46 | return ( 47 | 48 | {Object.keys(props.elements).map(key => ( 49 | {key} 54 | ))} 55 | 56 | ); 57 | }; 58 | 59 | export default Toggle; 60 | -------------------------------------------------------------------------------- /cairo/hash/circuit.cairo: -------------------------------------------------------------------------------- 1 | %builtins output pedersen range_check bitwise 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 5 | from starkware.cairo.common.serialize import serialize_word 6 | 7 | from cairo.utils.encrypt import (Encrypt, Decrypt, Ciphertext, get_hash_pedersen) 8 | 9 | struct HashOutput: 10 | member key_hash : felt 11 | 12 | member ciphertext_1 : felt 13 | member ciphertext_2 : felt 14 | 15 | member hash : felt 16 | member salt : felt 17 | end 18 | 19 | func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() -> (): 20 | alloc_locals 21 | 22 | local preimage 23 | local key 24 | 25 | %{ 26 | ids.preimage = program_input['preimage'] 27 | ids.key = program_input['key'] 28 | %} 29 | 30 | let output = cast(output_ptr, HashOutput*) 31 | let output_ptr = output_ptr + HashOutput.SIZE 32 | local output_ptr: felt* = output_ptr 33 | 34 | # proof of property (hash) 35 | let (preimage_hash) = get_hash_pedersen(preimage, 0) 36 | assert output.hash = preimage_hash 37 | assert output.salt = 0 38 | 39 | # hash of key 40 | let (key_hash) = get_hash_pedersen(key, 0) 41 | assert output.key_hash = key_hash 42 | 43 | # encryption of message 44 | let (ciphertext: Ciphertext) = Encrypt(preimage, key) 45 | assert output.ciphertext_1 = ciphertext.a 46 | assert output.ciphertext_2 = ciphertext.b 47 | 48 | return () 49 | end 50 | -------------------------------------------------------------------------------- /scripts/cairoCall.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | const contractName = process.argv[2]; 6 | const func = process.argv[3]; 7 | const type = process.argv[4]; 8 | const inputs = process.argv.slice(5); 9 | 10 | const cwd = process.cwd(); 11 | const directory = path.resolve(`${cwd}/cairo/contracts`); 12 | const contractAbi = JSON.parse(fs.readFileSync(`${directory}/${contractName}_abi.json`)); 13 | const CONTRACT_ADDRESS = JSON.parse(fs.readFileSync(`${directory}/${contractName}_address.json`)).address; 14 | 15 | if (type === 'invoke') { 16 | if (inputs.length > 0) { 17 | execSync(`starknet invoke \ --address ${CONTRACT_ADDRESS} \ --abi ./cairo/contracts/${contractName}_abi.json \ --function ${func} \ --inputs ${inputs.join(' ')}`, { 18 | stdio: 'inherit', 19 | }); 20 | } else { 21 | execSync(`starknet invoke \ --address ${CONTRACT_ADDRESS} \ --abi ./cairo/contracts/${contractName}_abi.json \ --function ${func}`, { 22 | stdio: 'inherit', 23 | }); 24 | } 25 | } else { 26 | if (inputs.length > 0) { 27 | execSync(`starknet call \ --address ${CONTRACT_ADDRESS} \ --abi ./cairo/contracts/${contractName}_abi.json \ --function ${func} \ --inputs ${inputs.join(' ')}`, { 28 | stdio: 'inherit', 29 | }); 30 | } else { 31 | execSync(`starknet call \ --address ${CONTRACT_ADDRESS} \ --abi ./cairo/contracts/${contractName}_abi.json \ --function ${func}`, { 32 | stdio: 'inherit', 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/utils/api.ts: -------------------------------------------------------------------------------- 1 | // params is given as a JSON 2 | export function get(endpoint) { 3 | return fetch(endpoint, { 4 | headers: { 5 | 'Access-Control-Allow-Origin': '*', 6 | }, 7 | }).then(res => { 8 | if(res.ok){ 9 | return res.json(); 10 | } else{ 11 | return res; 12 | } 13 | }); 14 | } 15 | 16 | //make sure headers includes 'Content-type': 'application/json' 17 | export function post(endpoint, params) { 18 | return fetch(endpoint, { 19 | method: 'post', 20 | headers: { 21 | 'Accept': 'application/json', 22 | 'Content-Type': 'application/json', 23 | 'Access-Control-Allow-Origin': '*', 24 | }, 25 | body: JSON.stringify(params) 26 | }).then(res => { 27 | if(res.ok){ 28 | return res.json(); 29 | } else{ 30 | return res 31 | } 32 | }); 33 | } 34 | 35 | export function put(endpoint, params) { 36 | return fetch(endpoint, { 37 | method: 'put', 38 | headers: { 39 | 'Accept': 'application/json', 40 | 'Content-Type': 'application/json' 41 | }, 42 | body: JSON.stringify(params) 43 | }).then(res => { 44 | if(res.ok){ 45 | return res.json() 46 | } else{ 47 | return res 48 | } 49 | }); 50 | } 51 | 52 | export function getJsonFromUrl(url) { 53 | if(!url) url = location.search; 54 | var query = url.substr(1); 55 | var result = {}; 56 | query.split("&").forEach(function(part) { 57 | var item = part.split("="); 58 | result[item[0]] = decodeURIComponent(item[1]); 59 | }); 60 | return result; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/components/Modal.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { Button } from './Button'; 5 | 6 | const ModalWrapper = styled.div` 7 | position: fixed; 8 | display: flex; 9 | flex-direction: column; 10 | top: 50%; 11 | left: 50%; 12 | height: 500px; 13 | width: 500px; 14 | background: white; 15 | border: 1px solid #ccc; 16 | transition: 1.1s ease-out; 17 | box-shadow: -2rem 2rem 2rem rgba(0, 0, 0, 0.2); 18 | filter: blur(0); 19 | transform: translate(-50%,-50%); 20 | opacity: 1; 21 | visibility: visible; 22 | padding: 10px; 23 | z-index: 1; 24 | `; 25 | 26 | const ModalButton = styled(Button)` 27 | margin-top: auto; 28 | `; 29 | 30 | const CloseIcon = styled.img` 31 | position: fixed; 32 | right: 10px; 33 | top: 10px; 34 | width: 30px; 35 | height: 30px; 36 | :hover { 37 | cursor: pointer; 38 | } 39 | `; 40 | 41 | type ModalProps = { 42 | onClose: () => void; 43 | show: boolean; 44 | buttonLabel: string; 45 | children: any; 46 | } 47 | 48 | export default function Modal(props: ModalProps) { 49 | const onClose = () => { 50 | props.onClose(); 51 | }; 52 | 53 | return ( 54 | <> 55 | {props.show ? 56 | 57 | <> 58 | {props.children} 59 | 60 | 61 | 62 | {props.buttonLabel} 63 | 64 | 65 | : null} 66 | 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Full goerli deploy including any permissions that need to be set. 3 | */ 4 | import { task } from 'hardhat/config'; 5 | import fs from 'fs'; 6 | import { save } from './utils'; 7 | 8 | let NETWORK: string; 9 | 10 | task('deploy', 'Full deployment', async (_taskArgs, hre) => { 11 | NETWORK = hre.network.name; 12 | console.log(`Deploying on ${NETWORK}`); 13 | 14 | if (!fs.existsSync(`./deployments/${NETWORK}`)) { 15 | fs.mkdirSync(`./deployments/${NETWORK}`, { recursive: true }); 16 | } 17 | 18 | console.log('Deploying Libraries'); 19 | const verifierFactory = await hre.ethers.getContractFactory('Verifier'); 20 | const verifier = await verifierFactory.deploy(); 21 | const pairingFactory = await hre.ethers.getContractFactory('Pairing'); 22 | const pairing = await pairingFactory.deploy(); 23 | await verifier.deployed(); 24 | await pairing.deployed(); 25 | 26 | console.log('Deploying Core'); 27 | const coreFactory = await hre.ethers.getContractFactory('Core', { 28 | libraries: { 29 | Verifier: verifier.address, 30 | }, 31 | }); 32 | const core = await coreFactory.deploy( 33 | '1141005542993923374493036937263227815234315019152226250678688752414333038841', 34 | '0xAB43bA48c9edF4C2C4bB01237348D1D7B28ef168', 35 | ); 36 | await core.deployed(); 37 | save('Core', core, NETWORK); 38 | 39 | console.log('Deploying Getters'); 40 | const gettersFactory = await hre.ethers.getContractFactory('Getters'); 41 | const getters = await gettersFactory.deploy(core.address); 42 | await getters.deployed(); 43 | save('Getters', getters, NETWORK); 44 | }); 45 | -------------------------------------------------------------------------------- /artifacts/@openzeppelin/contracts/access/Ownable.sol/Ownable.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "Ownable", 4 | "sourceName": "@openzeppelin/contracts/access/Ownable.sol", 5 | "abi": [ 6 | { 7 | "anonymous": false, 8 | "inputs": [ 9 | { 10 | "indexed": true, 11 | "internalType": "address", 12 | "name": "previousOwner", 13 | "type": "address" 14 | }, 15 | { 16 | "indexed": true, 17 | "internalType": "address", 18 | "name": "newOwner", 19 | "type": "address" 20 | } 21 | ], 22 | "name": "OwnershipTransferred", 23 | "type": "event" 24 | }, 25 | { 26 | "inputs": [], 27 | "name": "owner", 28 | "outputs": [ 29 | { 30 | "internalType": "address", 31 | "name": "", 32 | "type": "address" 33 | } 34 | ], 35 | "stateMutability": "view", 36 | "type": "function" 37 | }, 38 | { 39 | "inputs": [], 40 | "name": "renounceOwnership", 41 | "outputs": [], 42 | "stateMutability": "nonpayable", 43 | "type": "function" 44 | }, 45 | { 46 | "inputs": [ 47 | { 48 | "internalType": "address", 49 | "name": "newOwner", 50 | "type": "address" 51 | } 52 | ], 53 | "name": "transferOwnership", 54 | "outputs": [], 55 | "stateMutability": "nonpayable", 56 | "type": "function" 57 | } 58 | ], 59 | "bytecode": "0x", 60 | "deployedBytecode": "0x", 61 | "linkReferences": {}, 62 | "deployedLinkReferences": {} 63 | } 64 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | 2 | enum ZKTypes { 3 | SNARK = 'snark', 4 | STARK = 'stark', 5 | } 6 | 7 | enum ContentProperties { 8 | HASH = 'hash', 9 | BLUR = 'blur', 10 | DF = 'df', 11 | } 12 | 13 | enum EthFunctions { 14 | GET_URLS = 'getUrls', 15 | GET_URL_DATA = 'getUrlData', 16 | POST_URL = 'postUrl', 17 | GET_CIPHERTEXT = 'getCiphertext', 18 | BUY_TOKEN = 'buyToken', 19 | GET_TOKENS = 'getTokens', 20 | REDEEM = 'redeem', 21 | GET_OWNER = 'getOwner', 22 | CHECK_OWNERSHIP = 'checkOwnership', 23 | CHECK_NFT = 'checkNFT', 24 | CHECK_CREATOR = 'checkCreator', 25 | GET_CREATOR = 'getCreator', 26 | GET_PUBLIC_KEY = 'getPublicKey', 27 | GET_PROPERTY = 'getProperty', 28 | GET_PROPERTIES = 'getProperties', 29 | } 30 | 31 | enum TokenStates { 32 | UNPURCHASED = 'unpurchased', 33 | OWNED = 'owned', 34 | SELLER = 'seller', 35 | NULL = 'null', 36 | } 37 | 38 | interface Snark { 39 | proof: { 40 | pi_a: BigInt[], 41 | pi_b: BigInt[][], 42 | pi_c: BigInt[], 43 | }; 44 | publicSignals: BigInt[]; 45 | } 46 | const EmptySnark = { 47 | proof: { 48 | pi_a: [], 49 | pi_b: [[], []], 50 | pi_c: [], 51 | }, 52 | publicSignals: [], 53 | }; 54 | 55 | interface Stark { 56 | fact: string, 57 | programOutputs: BigInt[], 58 | } 59 | const EmptyStark = { 60 | fact: '', 61 | programOutputs: [], 62 | }; 63 | 64 | interface IpfsResponse { 65 | path: string; 66 | CID: any; 67 | size: number; 68 | } 69 | 70 | interface Ciphertext { 71 | iv: BigInt; 72 | data: BigInt[]; 73 | } 74 | 75 | 76 | export { 77 | ContentProperties, 78 | TokenStates, 79 | ZKTypes, 80 | Snark, 81 | EmptySnark, 82 | Stark, 83 | EmptyStark, 84 | IpfsResponse, 85 | Ciphertext, 86 | EthFunctions, 87 | }; 88 | -------------------------------------------------------------------------------- /circuits/utils/encrypt.circom: -------------------------------------------------------------------------------- 1 | include "../../node_modules/circomlib/circuits/mimc.circom" 2 | 3 | template EncryptBits(N) { 4 | signal private input plaintext[N]; 5 | signal input shared_key; 6 | signal output out[N+1]; 7 | 8 | component mimc = MultiMiMC7(N, 91); 9 | for (var i=0; i { 10 | if (i == 0) { 11 | return _; 12 | } else { 13 | return _[0].toUpperCase() + _.slice(1); 14 | } 15 | }).join(''); 16 | } 17 | 18 | export function upperCase(str) { 19 | return str.split('-').map((_, i) => { 20 | return _[0].toUpperCase() + _.slice(1); 21 | }).join(''); 22 | } 23 | 24 | export function getAddress(contract: string, network: string) { 25 | try { 26 | return JSON.parse(fs.readFileSync(`./deployments/${network}/${contract}.json`).toString()).address; 27 | } catch (err) { 28 | throw Error(`${contract} deployment on ${network} not found, run 'yarn deploy:${network}'`); 29 | } 30 | } 31 | 32 | export function getAccounts(network: string) { 33 | const files = fs.readdirSync(`./deployments/${network}`); 34 | return files.filter(file => file.slice(0, 7) === 'Account').map(file => { 35 | return file.split('-')[1].split('.')[0]; 36 | }); 37 | } 38 | 39 | export function parseCalldata(calldata: string, layer: number, network: string) { 40 | const _calldata = calldata ? calldata.split(',') : []; 41 | const accounts = getAccounts(network); 42 | return _calldata.map((input: string) => { 43 | if (accounts.includes(input)) { 44 | return BigInt(getAddress(`Account-${input}`, network)).toString(); 45 | } else { 46 | return input; 47 | } 48 | }); 49 | } 50 | 51 | export function save(name: string, contract: any, network: string) { 52 | fs.writeFileSync(`${DEPLOYMENTS_DIR}/${network}/${name}.json`, JSON.stringify({ 53 | 'address': contract.address, 54 | })); 55 | } 56 | -------------------------------------------------------------------------------- /cairo/contracts/ERC721.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.starknet.common.storage import Storage 4 | from starkware.cairo.common.cairo_builtins import HashBuiltin 5 | 6 | @storage_var 7 | func _owners(token_id: felt) -> (owners: felt): 8 | end 9 | 10 | @storage_var 11 | func _balances(address: felt) -> (balance: felt): 12 | end 13 | 14 | @storage_var 15 | func _tokenApprovals(token_id: felt) -> (address: felt): 16 | end 17 | 18 | @storage_var 19 | func _operatorApprovals(address1: felt, address2: felt) -> (bool: felt): 20 | end 21 | 22 | func balanceOf{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(owner: felt) -> (balance: felt): 23 | #require(owner != address(0), "ERC721: balance query for the zero address"); 24 | let (balance) = _balances.read(owner) 25 | return (balance) 26 | end 27 | 28 | func ownerOf{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(token_id: felt) -> (address: felt): 29 | let (owner) = _owners.read(token_id) 30 | #require(owner != address(0), "ERC721: owner query for nonexistent token"); 31 | return (owner) 32 | end 33 | 34 | func tokenURI(token_id: felt) -> (uri: felt): 35 | #require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); 36 | 37 | #string memory baseURI = _baseURI(); 38 | #return bytes(baseURI).length > 0 39 | #? string(abi.encodePacked(baseURI, tokenId.toString())) 40 | #: ''; 41 | end 42 | 43 | func _mint{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(to: felt, token_id: felt): 44 | #require(to != address(0), "ERC721: mint to the zero address"); 45 | #require(!_exists(tokenId), "ERC721: token already minted"); 46 | 47 | let (balance) = _balances.read(to) 48 | _balances.write(to, balance+1) 49 | _owners.write(token_id, to) 50 | 51 | return () 52 | end 53 | -------------------------------------------------------------------------------- /src/utils/parsers.ts: -------------------------------------------------------------------------------- 1 | import { ContentProperties, ZKTypes, Snark, Stark } from '../types'; 2 | 3 | 4 | const hashParser = (outputs): Output => { 5 | const _hash = BigInt(outputs[3]); 6 | const _ciphertext = { 7 | iv: BigInt(outputs[1]), 8 | data: [BigInt(outputs[2])], 9 | }; 10 | 11 | return { contentProperty: _hash, ciphertext: _ciphertext }; 12 | }; 13 | 14 | const dfParser = (outputs): Output => { 15 | const _hash = BigInt(outputs[4]); 16 | const _ciphertext = { 17 | iv: BigInt(outputs[1]), 18 | data: [ 19 | BigInt(outputs[2]), 20 | BigInt(outputs[3]), 21 | ], 22 | }; 23 | return { contentProperty: _hash, ciphertext: _ciphertext }; 24 | }; 25 | 26 | const blurParser = (outputs): Output => { 27 | const _blurredImage = 28 | outputs.slice(1, 17).map(Number); 29 | return { 30 | contentProperty: _blurredImage, 31 | ciphertext: _blurredImage, 32 | }; 33 | }; 34 | 35 | interface Output { 36 | contentProperty: any; 37 | ciphertext: any; 38 | } 39 | 40 | 41 | const snarkParser = 42 | (parser: (outputs: BigInt[]) => Output) => (proof: Snark): Output => { 43 | return parser(proof.publicSignals); 44 | }; 45 | 46 | const starkParser = (parser: (proof: BigInt[]) => Output) => (proof: Stark) => { 47 | return parser(proof.programOutputs); 48 | }; 49 | 50 | const SnarkParsers = { 51 | [ContentProperties.HASH]: snarkParser(hashParser), 52 | [ContentProperties.DF]: snarkParser(dfParser), 53 | [ContentProperties.BLUR]: snarkParser(blurParser), 54 | }; 55 | 56 | const StarkParsers = { 57 | [ContentProperties.HASH]: starkParser(hashParser), 58 | [ContentProperties.DF]: starkParser(dfParser), 59 | [ContentProperties.BLUR]: starkParser(blurParser), 60 | }; 61 | 62 | export const Parsers = { 63 | [ZKTypes.SNARK]: SnarkParsers, 64 | [ZKTypes.STARK]: StarkParsers, 65 | }; 66 | -------------------------------------------------------------------------------- /cairo/contracts/Storage.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | 4 | struct Ciphertext: 5 | member a: felt 6 | member b: felt 7 | end 8 | 9 | struct PublicKey: 10 | member a: felt 11 | member b: felt 12 | end 13 | 14 | struct Account: 15 | member public_key: felt 16 | member token_balance: felt 17 | end 18 | 19 | struct State: 20 | member token_balance: felt 21 | end 22 | 23 | ### USERS ### 24 | @storage_var 25 | func publicKeys(address: felt, index: felt) -> (public_key: felt): 26 | end 27 | 28 | 29 | ### URLS ### 30 | @storage_var 31 | func urlIndex() -> (index: felt): 32 | end 33 | @storage_var 34 | func urls(index: felt) -> (url: (felt, felt)): 35 | end 36 | @storage_var 37 | func _urlCreators(url1: felt, url2: felt) -> (creator: felt): 38 | end 39 | @storage_var 40 | func prices(url1: felt, url2: felt) -> (price: felt): 41 | end 42 | @storage_var 43 | func urlToHash(url1: felt, url2: felt) -> (hash: felt): 44 | end 45 | @storage_var 46 | func urlToProperty(url1: felt, url2: felt) -> (property: felt): 47 | end 48 | 49 | 50 | ### TOKENS ### 51 | @storage_var 52 | func numOfTokens(url1: felt, url2: felt) -> (index: felt): 53 | end 54 | @storage_var 55 | func _tokenIds() -> (res: felt): 56 | end 57 | @storage_var 58 | func _urlTokens(url1: felt, url2: felt, index: felt) -> (token: felt): 59 | end 60 | @storage_var 61 | func tokenUrl(token_id: felt) -> (url: (felt, felt)): 62 | end 63 | @storage_var 64 | func tokenToCiphertext(token_id: felt, index: felt) -> (ciphertext: felt): 65 | end 66 | @storage_var 67 | func redeemed(token_id: felt) -> (bool: felt): 68 | end 69 | 70 | 71 | ### PROPERTIES ### 72 | @storage_var 73 | func numOfProperties() -> (int: felt): 74 | end 75 | @storage_var 76 | func _propertyIds() -> (res: felt): 77 | end 78 | @storage_var 79 | func properties(property: felt) -> (id: felt): 80 | end 81 | @storage_var 82 | func _idProperties(id: felt) -> (property: felt): 83 | end 84 | -------------------------------------------------------------------------------- /src/components/Resize.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | 3 | type ResizeProps = { 4 | containerRef: any; 5 | contentRef: any; 6 | slice: any; 7 | display: string; 8 | children: any; 9 | } 10 | 11 | const Resize = (props: ResizeProps) => { 12 | let display; 13 | 14 | useEffect(() => { 15 | const getHash = () => { 16 | display = props.display ? props.display : ''; 17 | if (props.containerRef && props.contentRef) { 18 | check(); 19 | } 20 | }; 21 | getHash(); 22 | window.addEventListener('resize', getHash); 23 | }, [props]); 24 | 25 | const isOverflow = () => { 26 | var c = document.createElement('canvas'); 27 | var ctx = c.getContext('2d'); 28 | ctx.font = '20px times new roman'; 29 | var txt = display; 30 | const size = ctx.measureText(txt).width + 20; 31 | 32 | return props.containerRef.offsetWidth < size; 33 | }; 34 | 35 | const checkOverflow = (start, end) => { 36 | const middle = Math.floor((start + end) / 2); 37 | if (!middle) { 38 | props.contentRef.innerHTML = display; 39 | return; 40 | } 41 | display = props.slice(middle); 42 | const _isOverflow = isOverflow(); 43 | if (start === middle) { 44 | display = props.slice( 45 | end - (_isOverflow ? 3 : 1), 46 | ); 47 | props.contentRef.innerHTML = display; 48 | return; 49 | } 50 | if (_isOverflow) { 51 | checkOverflow(start, middle); 52 | } else { 53 | checkOverflow(middle, end); 54 | } 55 | }; 56 | 57 | const check = () => { 58 | if (isOverflow()) { 59 | checkOverflow(0, display.length - 1); 60 | } else { 61 | props.contentRef.innerHTML = display; 62 | } 63 | }; 64 | 65 | return ( 66 |
67 | {props.children} 68 |
69 | ); 70 | }; 71 | 72 | export default Resize; 73 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended", 9 | "plugin:@typescript-eslint/eslint-recommended" 10 | ], 11 | "globals": { 12 | "Atomics": "readonly", 13 | "SharedArrayBuffer": "readonly", 14 | "require": true, 15 | "process": true, 16 | "module": true, 17 | "BigInt": true, 18 | "Buffer": true 19 | }, 20 | "parser": "@typescript-eslint/parser", 21 | "parserOptions": { 22 | "ecmaFeatures": { 23 | "jsx": true 24 | }, 25 | "ecmaVersion": 2018, 26 | "sourceType": "module" 27 | }, 28 | "plugins": [ 29 | "react", 30 | "@typescript-eslint" 31 | ], 32 | "settings": { 33 | "react": { 34 | "version": "detect" 35 | } 36 | }, 37 | "ignorePatterns": [ 38 | "webpack.config.js", 39 | "test/*", 40 | "node_modules/*", 41 | "dist/*", 42 | "public/*", 43 | "scripts/*" 44 | ], 45 | "rules": { 46 | "indent": [ 47 | "error", 48 | 2, 49 | { "SwitchCase": 1 } 50 | ], 51 | "linebreak-style": [ 52 | "error", 53 | "unix" 54 | ], 55 | "quotes": [ 56 | "error", 57 | "single" 58 | ], 59 | "semi": [ 60 | "error", 61 | "always" 62 | ], 63 | "max-len": [ 64 | "error", 65 | { 66 | "code": 80, 67 | "ignoreUrls": true 68 | } 69 | ], 70 | "object-curly-spacing": ["error", "always", { 71 | "arraysInObjects": true, 72 | "objectsInObjects": true 73 | }], 74 | "comma-dangle": ["error", "always-multiline"], 75 | "no-unused-vars": "off", 76 | "@typescript-eslint/no-unused-vars": ["error"], 77 | "no-trailing-spaces": ["error"], 78 | "no-inner-declarations": "off" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /cairo/utils/mimc.cairo: -------------------------------------------------------------------------------- 1 | struct Field: 2 | member x : felt 3 | end 4 | 5 | func iter( 6 | field_elements : Field*, 7 | k : felt, 8 | t7_1 : felt, 9 | n_steps 10 | ) -> (final_t, final_t6): 11 | 12 | # why doesn't the following work? 13 | # let t = k + t7_1 + field_elements.x 14 | 15 | [ap] = field_elements.x; ap++ 16 | [ap] = t7_1 + [ap-1]; ap++ # t 17 | [ap] = [ap-1]*[ap-1]; ap++ # t2 18 | [ap] = [ap-1]*[ap-1]; ap++ # t4 19 | [ap] = [ap-1]*[ap-1]; ap++ # t6 20 | 21 | if n_steps != 0: 22 | let t7 = [ap-4]*[ap-1] 23 | iter( 24 | field_elements + Field.SIZE, 25 | k, 26 | t7, 27 | n_steps - 1 28 | ) 29 | ret 30 | else: 31 | return (final_t=[ap-4], final_t6=[ap-1]) 32 | end 33 | end 34 | 35 | func mimc7( 36 | field_elements : Field*, 37 | x_in : felt, 38 | k : felt, 39 | nrounds : felt 40 | ) -> (out): 41 | alloc_locals 42 | 43 | let t = k + x_in 44 | let t2 = t*t 45 | let t4 = t2*t2 46 | let t6 = t4*t4 47 | 48 | let t7 = t6*t 49 | 50 | let (__fp__, _) = get_fp_and_pc() 51 | let (final_t, final_t6) = iter( 52 | field_elements + Field.SIZE, 53 | k, 54 | t7, 55 | nrounds - 2 56 | ) 57 | [ap] = final_t6; ap++ 58 | [ap] = final_t; ap++ 59 | [ap] = [ap-1]*[ap-2]; ap++ 60 | [ap] = [ap-1] + k; ap++ 61 | return (out=[ap-1]) 62 | end 63 | 64 | func main(): 65 | alloc_locals 66 | 67 | local field_elements : Field* 68 | local x_in : felt 69 | local k : felt 70 | local nrounds : felt 71 | 72 | %{ 73 | field_elements1 = program_input['field_elements'] 74 | x_in1 = program_input['x_in'] 75 | k1 = program_input['k'] 76 | nrounds1 = program_input['nrounds'] 77 | 78 | ids.field_elements = field_elements = segments.add() 79 | for i, val in enumerate(field_elements1): 80 | memory[field_elements + i] = val 81 | 82 | ids.x_in = program_input['x_in'] 83 | ids.k = program_input['k'] 84 | ids.nrounds = program_inputs['nrounds'] 85 | %} 86 | 87 | let (out) = mimc7(field_elements, x_in, k, nrounds) 88 | [ap] = out; ap++ 89 | %{ 90 | print('Hash', memory[ap-1]) 91 | %} 92 | return () 93 | end 94 | -------------------------------------------------------------------------------- /cairo/encryption/circuit.cairo: -------------------------------------------------------------------------------- 1 | %builtins output pedersen range_check bitwise 2 | 3 | from starkware.cairo.common.cairo_builtins import HashBuiltin 4 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 5 | from starkware.cairo.common.serialize import serialize_word 6 | 7 | from cairo.utils.ec_math import ec_mult, Point 8 | from cairo.utils.encrypt import (Encrypt, Decrypt, Ciphertext, get_hash_pedersen) 9 | 10 | 11 | struct PublicKey: 12 | member a : felt 13 | member b : felt 14 | end 15 | 16 | struct EncryptionOutput: 17 | member hash : felt 18 | 19 | member encryption_1 : felt 20 | member encryption_2 : felt 21 | 22 | member public_key_1 : felt 23 | member public_key_2 : felt 24 | end 25 | 26 | func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() -> (): 27 | alloc_locals 28 | 29 | local key 30 | local seller_private_key 31 | local buyer_public_key: PublicKey* 32 | 33 | %{ 34 | ids.key = program_input['key'] 35 | ids.seller_private_key = program_input['seller_private_key'] 36 | 37 | _buyer_public_key = program_input['buyer_public_key'] 38 | ids.buyer_public_key = buyer_public_key = segments.add() 39 | for i, val in enumerate(_buyer_public_key): 40 | memory[buyer_public_key + i] = val 41 | %} 42 | 43 | let output = cast(output_ptr, EncryptionOutput*) 44 | let output_ptr = output_ptr + EncryptionOutput.SIZE 45 | local output_ptr: felt* = output_ptr 46 | 47 | assert output.public_key_1 = buyer_public_key.a 48 | assert output.public_key_2 = buyer_public_key.b 49 | 50 | # output key hash 51 | let (hash_out) = get_hash_pedersen(key, 0) 52 | assert output.hash = hash_out 53 | local pedersen_ptr: HashBuiltin* = pedersen_ptr 54 | 55 | # generate shared key 56 | let public_key_point = Point(x=buyer_public_key.a, y=buyer_public_key.b) 57 | let (point: Point) = ec_mult(seller_private_key, public_key_point) 58 | let shared_key = point.x 59 | 60 | # encrypt key 61 | let (encryption: Ciphertext) = Encrypt(key, shared_key) 62 | 63 | assert output.encryption_1 = encryption.a 64 | assert output.encryption_2 = encryption.b 65 | 66 | return () 67 | end 68 | -------------------------------------------------------------------------------- /src/components/text.tsx: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components'; 2 | 3 | const textWeights = { 4 | light: '\'wght\' 300', 5 | regular: '\'wght\' 400', 6 | medium: '\'wght\' 500', 7 | bold: '\'wght\' 700', 8 | }; 9 | 10 | const textAlignment = { 11 | left: 'left', 12 | right: 'right', 13 | center: 'center', 14 | justify: 'justify', 15 | initial: 'initial', 16 | inherent: 'inherent', 17 | }; 18 | 19 | const textStyles = { 20 | normal: 'normal', 21 | italic: 'italic', 22 | }; 23 | 24 | const textTransformations = { 25 | uppercase: 'uppercase', 26 | lowercase: 'lowercase', 27 | capitalize: 'capitalize', 28 | }; 29 | 30 | const Text = props => { 31 | const color = props.colored ? 32 | props.theme.color[props.colored] : props.theme.color.darkText; 33 | const weight = props.weight && textWeights[props.weight]; 34 | const alignment = props.align && textAlignment[props.align]; 35 | const transform = props.transform && textTransformations[props.transform]; 36 | const style = props.styled && textStyles[props.styled]; 37 | const font = props.theme.fontFamily; 38 | 39 | return css` 40 | color: ${color}; 41 | font-variation-settings: ${weight}; 42 | text-align: ${alignment}; 43 | text-transform: ${transform}; 44 | font-style: ${style}; 45 | font-family: ${font}; 46 | `; 47 | }; 48 | 49 | export const Small = styled.div` 50 | ${Text}; 51 | font-size: ${props => props.theme.text.small.size}; 52 | line-height: ${props => props.theme.text.small.lineHeight}; 53 | `; 54 | 55 | export const Medium = styled.div` 56 | ${Text}; 57 | font-size: ${props => props.theme.text.medium.size}; 58 | line-height: ${props => props.theme.text.medium.lineHeight}; 59 | `; 60 | 61 | export const Large = styled.div` 62 | ${Text}; 63 | font-size: ${props => props.theme.text.large.size}; 64 | line-height: ${props => props.theme.text.large.lineHeight}; 65 | `; 66 | 67 | export const Header = styled.div` 68 | ${Text}; 69 | font-size: ${props => props.theme.text.xlarge.size}; 70 | line-height: ${props => props.theme.text.xlarge.lineHeight}; 71 | font-variation-settings: ${textWeights.bold}; 72 | display: flex; 73 | justify-content: center; 74 | `; 75 | -------------------------------------------------------------------------------- /artifacts/contracts/Core.sol/DarkForestCore.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "DarkForestCore", 4 | "sourceName": "contracts/Core.sol", 5 | "abi": [ 6 | { 7 | "inputs": [ 8 | { 9 | "internalType": "uint256", 10 | "name": "key", 11 | "type": "uint256" 12 | } 13 | ], 14 | "name": "planets", 15 | "outputs": [ 16 | { 17 | "components": [ 18 | { 19 | "internalType": "address", 20 | "name": "owner", 21 | "type": "address" 22 | }, 23 | { 24 | "internalType": "bool", 25 | "name": "isHomePlanet", 26 | "type": "bool" 27 | } 28 | ], 29 | "internalType": "struct DarkForestCore.Planet", 30 | "name": "", 31 | "type": "tuple" 32 | } 33 | ], 34 | "stateMutability": "view", 35 | "type": "function" 36 | }, 37 | { 38 | "inputs": [], 39 | "name": "s", 40 | "outputs": [ 41 | { 42 | "components": [ 43 | { 44 | "internalType": "uint256", 45 | "name": "PLANETHASH_KEY", 46 | "type": "uint256" 47 | } 48 | ], 49 | "internalType": "struct DarkForestCore.SnarkConstants", 50 | "name": "snarkConstants", 51 | "type": "tuple" 52 | } 53 | ], 54 | "stateMutability": "view", 55 | "type": "function" 56 | }, 57 | { 58 | "inputs": [], 59 | "name": "snarkConstants", 60 | "outputs": [ 61 | { 62 | "components": [ 63 | { 64 | "internalType": "uint256", 65 | "name": "PLANETHASH_KEY", 66 | "type": "uint256" 67 | } 68 | ], 69 | "internalType": "struct DarkForestCore.SnarkConstants", 70 | "name": "", 71 | "type": "tuple" 72 | } 73 | ], 74 | "stateMutability": "view", 75 | "type": "function" 76 | } 77 | ], 78 | "bytecode": "0x", 79 | "deployedBytecode": "0x", 80 | "linkReferences": {}, 81 | "deployedLinkReferences": {} 82 | } 83 | -------------------------------------------------------------------------------- /scripts/buildConfig.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import config from '../config/config'; 4 | import { upperCase } from './utils'; 5 | 6 | const cwd = process.cwd(); 7 | 8 | let network; 9 | if (config.env === 'production') { 10 | network = config.network; 11 | } else { 12 | network = 'localhost'; 13 | } 14 | 15 | const deployedContractsDir = path.resolve(`${cwd}/deployments/${network}`); 16 | const contracts = fs.readdirSync(deployedContractsDir).map(_ => _.split('.')[0]); 17 | contracts.forEach(contract => { 18 | const contractName = upperCase(contract); 19 | const abiJson = JSON.parse(fs.readFileSync(`./artifacts/contracts/${contractName}.sol/${contractName}.json`).toString()); 20 | let addressJson; 21 | addressJson = JSON.parse(fs.readFileSync(`./deployments/${network}/${contractName}.json`).toString()); 22 | 23 | if (!fs.existsSync('./public/contracts')) { 24 | fs.mkdirSync('./public/contracts', { recursive: true }); 25 | } 26 | fs.writeFileSync('./config/config.json', JSON.stringify(config)); 27 | fs.writeFileSync(`./public/contracts/${contractName}.json`, JSON.stringify({ 28 | address: addressJson.address, 29 | abi: abiJson.abi, 30 | })); 31 | 32 | if (!fs.existsSync('./public/circuits/key')) { 33 | fs.mkdirSync('./public/circuits/key', { recursive: true }); 34 | } 35 | if (!fs.existsSync('./public/circuits/verification_key')) { 36 | fs.mkdirSync('./public/circuits/verification_key', { recursive: true }); 37 | } 38 | if (!fs.existsSync('./public/circuits/wasm')) { 39 | fs.mkdirSync('./public/circuits/wasm', { recursive: true }); 40 | } 41 | }); 42 | 43 | const compiledCircuitsDir = path.resolve(`${cwd}/compiledCircuits`); 44 | const circuits = fs.readdirSync(compiledCircuitsDir); 45 | circuits.forEach(circuit => { 46 | fs.copyFileSync( 47 | `${compiledCircuitsDir}/${circuit}/circuit.wasm`, 48 | `${cwd}/public/circuits/wasm/${upperCase(circuit)}.wasm`, 49 | ); 50 | fs.copyFileSync( 51 | `${compiledCircuitsDir}/${circuit}/verification_key.json`, 52 | `${cwd}/public/circuits/verification_key/${upperCase(circuit)}.json`, 53 | ); 54 | fs.copyFileSync( 55 | `${compiledCircuitsDir}/${circuit}/circuit.zkey`, 56 | `${cwd}/public/circuits/key/${upperCase(circuit)}.zkey`, 57 | ); 58 | }); 59 | -------------------------------------------------------------------------------- /src/pages/ChooseUser.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import config from '../../config'; 5 | import { Large } from '../components/text'; 6 | import eth from '../utils/ethAPI'; 7 | 8 | const Title = styled(Large)` 9 | margin-bottom: 10px; 10 | `; 11 | 12 | const ChooseUserWrapper = styled.div` 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | margin-left: 35%; 17 | margin-right: 35%; 18 | margin-top: 10%; 19 | `; 20 | 21 | const AddressWrapper = styled.div` 22 | display: flex; 23 | flex-direction: row; 24 | justify-content: space-between; 25 | width: calc(100%); 26 | height: 50px; 27 | background-color: ${props => props.theme.color.white}; 28 | border: ${props => `1px solid ${props.theme.color.grey30}`}; 29 | margin-bottom: 10px; 30 | border-radius: 4px; 31 | padding: 10px; 32 | padding-left: 20px; 33 | cursor: pointer; 34 | align-items: center; 35 | :hover { 36 | background-color: ${props => props.theme.color.grey10}; 37 | } 38 | `; 39 | 40 | const AddressText = styled(Large)` 41 | font-size: 25px; 42 | `; 43 | 44 | type AddressProps = { 45 | key: string; 46 | name: string; 47 | onClick: () => void; 48 | }; 49 | 50 | const Address = (props: AddressProps) => { 51 | return ( 52 | 53 | {props.name.slice(0,6)} 54 | 55 | ); 56 | }; 57 | 58 | const ChooseUser = (props) => { 59 | const [addresses, setAddresses] = useState([]); 60 | 61 | useEffect(() => { 62 | if (props.signer) { 63 | props.signer.provider.listAccounts().then(setAddresses); 64 | } 65 | }, [props.signer]); 66 | 67 | const sendToTokens = () => { 68 | props.history.push('/tokens'); 69 | }; 70 | 71 | const selectAddress = (address: string) => () => { 72 | eth.setSigner(address); 73 | sendToTokens(); 74 | }; 75 | 76 | return ( 77 | 78 | Select which address to use 79 | {addresses.map(address =>
)} 84 | 85 | ); 86 | }; 87 | 88 | 89 | export default ChooseUser; 90 | -------------------------------------------------------------------------------- /src/pages/Content.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import DarkForest from './content/DarkForest'; 5 | import Blur from './content/Blur'; 6 | import Hash from './content/Hash'; 7 | 8 | import { 9 | ContentProperties, 10 | Ciphertext, 11 | } from '../types'; 12 | import { 13 | InputProps, 14 | ContentSkeleton, 15 | } from '../types/content'; 16 | 17 | 18 | const content: Record = { 19 | [ContentProperties.HASH]: Hash, 20 | [ContentProperties.BLUR]: Blur, 21 | [ContentProperties.DF]: DarkForest, 22 | }; 23 | 24 | interface ContentInputProps extends InputProps { 25 | property: ContentProperties; 26 | } 27 | 28 | const ContentInput = (props: ContentInputProps) => { 29 | 30 | const PropertyInputElement = content[props.property].input; 31 | 32 | return ( 33 | <> 34 | 38 | 39 | ); 40 | }; 41 | 42 | const ContentWrapper = styled.div` 43 | display: flex; 44 | flex-direction: row; 45 | width: 100%; 46 | background-color: ${props => props.theme.color.grey10}; 47 | margin-top: 10px; 48 | margin-bottom: 10px; 49 | `; 50 | 51 | type ContentProps = { 52 | property: string; 53 | zk: string; 54 | content: { 55 | cipher: number[] | Ciphertext, 56 | property: number[] | BigInt, 57 | }; 58 | secretKey: BigInt | null; 59 | } 60 | 61 | const Content = (props: ContentProps) => { 62 | const [message, setMessage] = useState(null); 63 | 64 | const { 65 | display, 66 | assertContent, 67 | assertMessage, 68 | decrypt, 69 | } = content[props.property]; 70 | 71 | useEffect(() => { 72 | assertContent(props.content); 73 | if (props.secretKey) { 74 | decrypt( 75 | props.zk, 76 | props.content.cipher, 77 | props.secretKey, 78 | ).then(_message => { 79 | assertMessage(_message); 80 | setMessage(_message); 81 | }); 82 | } 83 | }, [props]); 84 | 85 | const PropertyElement = display; 86 | 87 | return ( 88 | 89 | 90 | 91 | ); 92 | }; 93 | 94 | export { 95 | Content, 96 | ContentInput, 97 | content as ContentElements, 98 | }; 99 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, jsonify 2 | from flask_cors import CORS 3 | from starkware.crypto.signature.fast_pedersen_hash import pedersen_hash 4 | from starkware.cairo.bootloader.generate_fact import get_program_output 5 | from starkware.cairo.sharp.sharp_client import init_client 6 | from starkware.cairo.bootloader.hash_program import compute_program_hash_chain 7 | from starkware.crypto.signature.math_utils import ec_add, ec_mult 8 | from starkware.crypto.signature.signature import get_random_private_key, private_key_to_ec_point_on_stark_curve, FIELD_PRIME 9 | 10 | import subprocess 11 | import logging 12 | import sys 13 | import os 14 | import json 15 | import re 16 | 17 | app = Flask('test') 18 | CORS(app, resources={r'/*': {'origins': '*'}}) 19 | 20 | app.logger.setLevel(logging.INFO) 21 | 22 | sharp_client = init_client(bin_dir='', node_rpc_url='https://goerli.infura.io/v3/68283459972f4c5594019a8878ef1003') 23 | program = sharp_client.compile_cairo(source_code_path='/home/nulven/CairoMarketplace/cairo/encryption/circuit.cairo') 24 | print(sharp_client.contract_client.contract.address) 25 | print(compute_program_hash_chain(program)) 26 | 27 | 28 | @app.route('/prove', methods=['POST']) 29 | def prove(): 30 | circuit = request.json.get('circuit') 31 | inputs = request.json.get('inputs') 32 | parsed_inputs = {} 33 | for key, value in inputs.items(): 34 | if type(value) is list: 35 | parsed_inputs.update({key: [int(_) for _ in value]}) 36 | else: 37 | parsed_inputs[key] = int(value) 38 | print(circuit, parsed_inputs) 39 | with open('/home/nulven/CairoMarketplace/cairo/'+circuit+'/input.json', 'w') as f: 40 | json.dump(parsed_inputs, f) 41 | 42 | program = sharp_client.compile_cairo(source_code_path='/home/nulven/CairoMarketplace/cairo/' + circuit + '/circuit.cairo') 43 | print('PROGRAM') 44 | cairo_pie = sharp_client.run_program( 45 | program=program, program_input_path='/home/nulven/CairoMarketplace/cairo/' + circuit + '/input.json') 46 | print('CAIRO PIE') 47 | job_key = sharp_client.submit_cairo_pie(cairo_pie=cairo_pie) 48 | print('JOB KEY') 49 | 50 | fact = sharp_client.get_fact(cairo_pie) 51 | output = get_program_output(cairo_pie) 52 | output_new = [str(_) for _ in output] 53 | print(output_new) 54 | return jsonify({'res': {'programOutputs': output_new, 'fact': fact}}) 55 | 56 | app.run(host='localhost', port=5002, debug=True) 57 | -------------------------------------------------------------------------------- /cairo/contracts/utils.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | 3 | from starkware.starknet.common.storage import Storage 4 | from starkware.cairo.common.cairo_builtins import (HashBuiltin, SignatureBuiltin) 5 | from cairo.contracts.ERC721 import (ownerOf, _mint) 6 | from cairo.contracts.ERC721URIStorage import (_setTokenURI, tokenURI) 7 | from cairo.contracts.Storage import ( 8 | Ciphertext, PublicKey, 9 | publicKeys, urlIndex, urls, _urlCreators, prices, urlToHash, urlToProperty, 10 | numOfTokens, _tokenIds, _urlTokens, tokenUrl, tokenToCiphertext, redeemed, 11 | numOfProperties, _propertyIds, properties, _idProperties 12 | ) 13 | 14 | 15 | func _setProperty{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 16 | url1: felt, 17 | url2: felt, 18 | property: felt 19 | ) -> (bool: felt): 20 | 21 | let (propertyId) = properties.read(property) 22 | # assert that property exists 23 | 24 | urlToProperty.write(url1, url2, property) 25 | 26 | return (bool=1) 27 | end 28 | 29 | func _mintToken{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 30 | to: felt, 31 | url1: felt, 32 | url2: felt 33 | ) -> (): 34 | 35 | let (tokenId) = _tokenIds.read() 36 | let url: (felt, felt) = (url1, url2) 37 | let (length) = numOfTokens.read(url[0], url[1]) 38 | 39 | _urlTokens.write(url[0], url[1], length, tokenId) 40 | numOfTokens.write(url[0], url[1], length+1) 41 | _tokenIds.write(tokenId+1) 42 | tokenUrl.write(tokenId, url) 43 | 44 | _mint(to, tokenId) 45 | _setTokenURI(tokenId, url[0], url[1]) 46 | 47 | return () 48 | end 49 | 50 | func addPublicKey{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 51 | address: felt, 52 | public_key_a: felt, 53 | public_key_b: felt 54 | ) -> (bool: felt): 55 | 56 | # assert public key is not recorded 57 | let (public_key_a) = publicKeys.read(address, 0) 58 | assert public_key_a = 0 59 | 60 | publicKeys.write(address, 0, public_key_a) 61 | publicKeys.write(address, 1, public_key_b) 62 | 63 | return (bool=1) 64 | end 65 | 66 | func checkRedeem{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}( 67 | seller: felt, 68 | tokenId: felt 69 | ) -> (bool: felt): 70 | 71 | let (url) = tokenUrl.read(tokenId) 72 | let (creator) = _urlCreators.read(url[0], url[1]) 73 | assert seller = creator 74 | 75 | # assert coin is not redeemed 76 | let (redeem) = redeemed.read(tokenId) 77 | assert redeem = 0 78 | 79 | return (bool=1) 80 | end 81 | -------------------------------------------------------------------------------- /cairo/utils/ec_math.cairo: -------------------------------------------------------------------------------- 1 | from starkware.cairo.common.cairo_builtins import BitwiseBuiltin 2 | from starkware.cairo.common.math_cmp import is_nn 3 | from starkware.cairo.common.math import unsigned_div_rem 4 | from starkware.cairo.common.bitwise import bitwise_and 5 | from starkware.cairo.common.registers import get_fp_and_pc 6 | from starkware.cairo.common.alloc import alloc 7 | from cairo.utils.math import (MontgomeryDouble, Montgomery2Edwards, Edwards2Montgomery, MontgomeryAdd, BabyAdd) 8 | 9 | 10 | struct Point: 11 | member x: felt 12 | member y: felt 13 | end 14 | 15 | func div_mod{range_check_ptr}(n: felt, m: felt) -> (out: felt): 16 | alloc_locals 17 | 18 | let _m = m 19 | 20 | local a: felt 21 | local b: felt 22 | local c: felt 23 | %{ 24 | from sympy.core.numbers import igcdex 25 | from starkware.crypto.signature.signature import FIELD_PRIME 26 | _a, _b, _c = igcdex(ids._m, FIELD_PRIME) 27 | ids.a = _a % FIELD_PRIME 28 | ids.b = _b % FIELD_PRIME 29 | ids.c = _c % FIELD_PRIME 30 | %} 31 | let num = n*a 32 | return (out=num) 33 | end 34 | 35 | func ec_add{range_check_ptr}(point1: Point, point2: Point) -> (out: Point): 36 | let a = point1.y - point2.y 37 | let b = point1.x - point2.x 38 | let (m) = div_mod(a, b) 39 | 40 | let m_squared = m*m 41 | let c = m_squared - point1.x 42 | let x = c - point2.x 43 | 44 | let d = point1.x - x 45 | let e = m*d 46 | let y = e - point1.y 47 | 48 | tempvar out = Point(x=x, y=y) 49 | return (out) 50 | end 51 | 52 | func ec_double{range_check_ptr}(point: Point, alpha: felt) -> (out: Point): 53 | let point_squared = point.x*point.x 54 | let a1 = 3*point_squared 55 | let a2 = a1 + alpha 56 | let b = 2*point.y 57 | let (m) = div_mod(a2, b) 58 | let m_squared = m*m 59 | let c = 2*point.x 60 | let x = m_squared - c 61 | 62 | let d = point.x-x 63 | let e = m*d 64 | let y = e-point.y 65 | 66 | tempvar out = Point(x=x, y=y) 67 | return (out) 68 | end 69 | 70 | func ec_mult{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}( 71 | m: felt, 72 | point: Point 73 | ) -> (out: Point): 74 | alloc_locals 75 | 76 | if m == 1: 77 | return (out=point) 78 | end 79 | let (rem) = bitwise_and(m, 1) 80 | if rem == 0: 81 | let (dbl) = ec_double(point, 1) 82 | let m_over_2 = m/2 83 | let (out) = ec_mult(m_over_2, dbl) 84 | return (out) 85 | end 86 | let (mult) = ec_mult(m-1, point) 87 | let (add) = ec_add(mult, point) 88 | return (out=add) 89 | end 90 | -------------------------------------------------------------------------------- /contracts/ContractStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "@openzeppelin/contracts/utils/Counters.sol"; 5 | import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 6 | 7 | contract ContractStorage { 8 | struct Content { 9 | uint256 id; 10 | string url; 11 | string property; 12 | uint256 keyHash; 13 | address creator; 14 | uint256 price; 15 | string zk; 16 | } 17 | 18 | struct Token { 19 | uint256 id; 20 | uint256 contentId; 21 | uint256 ciphertext1; 22 | uint256 ciphertext2; 23 | bool redeemed; 24 | } 25 | 26 | using Counters for Counters.Counter; 27 | 28 | // URLS 29 | mapping(string => uint256) public contentIds; 30 | Content[] public contents; 31 | 32 | // TOKENS 33 | Counters.Counter public _tokenIds; 34 | Token[] public tokens; 35 | mapping(uint256 => EnumerableSet.UintSet) internal contentTokenIds; 36 | 37 | // PROPERTIES 38 | Counters.Counter public _propertyIds; 39 | mapping(string => uint256) public properties; 40 | mapping(uint256 => string) public _idProperties; 41 | 42 | // USERS 43 | mapping(address => mapping(string => uint256[2])) public publicKeys; 44 | 45 | function getPublicKey(address _address, string calldata zk) public view returns (uint256[2] memory publicKey) { 46 | return publicKeys[_address][zk]; 47 | } 48 | 49 | function getContent(uint256 contentId) public view returns (Content memory) { 50 | return contents[contentId]; 51 | } 52 | 53 | function contentCount() public view returns (uint256) { 54 | return contents.length; 55 | } 56 | 57 | function getToken(uint id) public view returns (Token memory) { 58 | return tokens[id]; 59 | } 60 | 61 | function getTokenIds(uint256 contentId) public view returns (uint256[] memory) { 62 | EnumerableSet.UintSet storage _tokenIds = contentTokenIds[contentId]; 63 | uint tokenCount = EnumerableSet.length(_tokenIds); 64 | uint256[] memory tokenIds = new uint256[](tokenCount); 65 | for (uint256 i = 0; i < tokenCount; i++) { 66 | tokenIds[i] = EnumerableSet.at(_tokenIds, i); 67 | } 68 | return tokenIds; 69 | } 70 | 71 | function getNumOfProperties() public view returns (uint256 length) { 72 | return _propertyIds.current(); 73 | } 74 | 75 | function incrementProperty() public returns (bool) { 76 | _propertyIds.increment(); 77 | return true; 78 | } 79 | 80 | function getProperty(uint256 id) public view returns (string memory) { 81 | return _idProperties[id]; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/utils/errors.ts: -------------------------------------------------------------------------------- 1 | class CustomError extends Error { 2 | constructor(json: { name: string, message: string }) { 3 | super(json.message); 4 | this.name = json.name; 5 | } 6 | } 7 | 8 | enum Errors { 9 | HashIsPosted = 'HashIsPosted', 10 | EthIsRedeemed = 'EthIsRedeemed', 11 | CiphertextNotReady = 'CiphertextNotReady', 12 | InvalidProof = 'InvalidProof', 13 | IncorrectHash = 'IncorrectHash', 14 | IncorrectPublicKey = 'IncorrectPublicKey', 15 | SolidityError = 'SolidityError', 16 | PropertyDoesNotExist = 'PropertyDoesNotExist', 17 | } 18 | 19 | const ErrorNames = { 20 | [Errors.HashIsPosted]: 'HASH_IS_POSTED', 21 | [Errors.EthIsRedeemed]: 'ETH_IS_REDEEMED', 22 | [Errors.CiphertextNotReady]: 'CIPHERTEXT_NOT_READY', 23 | [Errors.InvalidProof]: 'INVALID_PROOF', 24 | [Errors.IncorrectHash]: 'INCORRECT_HASH', 25 | [Errors.IncorrectPublicKey]: 'INCORRECT_PUBLIC_KEY', 26 | [Errors.PropertyDoesNotExist]: 'PROPERTY_DOES_NOT_EXIST', 27 | [Errors.SolidityError]: 'SOLIDITY_ERROR', 28 | }; 29 | 30 | const ErrorMessages = { 31 | [Errors.HashIsPosted]: () => 'The hash has already been posted.', 32 | [Errors.EthIsRedeemed]: 33 | () => 'The Eth for this token has already been redeemed.', 34 | [Errors.CiphertextNotReady]: 35 | () => 'The Ciphertext for this token has not been posted yet.', 36 | [Errors.InvalidProof]: () => 'Invalid ZK proof.', 37 | [Errors.IncorrectHash]: () => 'Proof created using the incorrect hash.', 38 | [Errors.IncorrectPublicKey]: 39 | () => 'Proof created using the incorrect public key.', 40 | [Errors.PropertyDoesNotExist]: () => 'Property does not exist.', 41 | [Errors.SolidityError]: (error: string) => `Solidity error: ${error}`, 42 | }; 43 | 44 | const SolidityErrors = { 45 | 'revert Hash already posted': Errors.HashIsPosted, 46 | 'revert ETH already redeemed': Errors.EthIsRedeemed, 47 | 'revert Ciphertext not posted yet': Errors.CiphertextNotReady, 48 | 'revert Proof invalid!': Errors.InvalidProof, 49 | 'revert Incorrect hash': Errors.IncorrectHash, 50 | 'revert Used wrong public key': Errors.IncorrectPublicKey, 51 | 'revert Property does not exist': Errors.PropertyDoesNotExist, 52 | }; 53 | 54 | function createErrors(): Record CustomError> { 55 | const errors = {}; 56 | for (const error in Errors) { 57 | errors[error] = (...args) => { 58 | return new CustomError({ 59 | name: ErrorNames[error], 60 | message: ErrorMessages[error](...args), 61 | }); 62 | }; 63 | } 64 | // @ts-ignore 65 | return errors; 66 | } 67 | 68 | const OurErrors = createErrors(); 69 | 70 | export { 71 | OurErrors, 72 | SolidityErrors, 73 | }; 74 | -------------------------------------------------------------------------------- /crypto/index.js: -------------------------------------------------------------------------------- 1 | const { randomBytes } = require('crypto'); 2 | const { prv2pub } = require('./eddsa'); 3 | const { mulPointEscalar } = require('./babyjub'); 4 | 5 | const STARK_FIELD_SIZE = BigInt('3618502788666131213697322783095070105623107215331596699973092056135872020481'); 6 | 7 | const lim = BigInt('0x10000000000000000000000000000000000000000000000000000000000000000'); 8 | 9 | const min = (lim - STARK_FIELD_SIZE) % STARK_FIELD_SIZE; 10 | 11 | const genRandomBabyJubValue = () => { 12 | 13 | let rand; 14 | while (true) { 15 | rand = BigInt('0x' + randomBytes(32).toString('hex')); 16 | 17 | if (rand >= min) { 18 | break; 19 | } 20 | } 21 | 22 | const privKey = rand % STARK_FIELD_SIZE; 23 | //assert(privKey < STARK_FIELD_SIZE); 24 | 25 | return privKey; 26 | }; 27 | 28 | /* 29 | type PrivKey = BigInt 30 | type PubKey = BigInt[] 31 | 32 | interface Keypair { 33 | privKey: PrivKey; 34 | pubKey: PubKey; 35 | } 36 | */ 37 | 38 | const genPrivKey = () => { 39 | 40 | return genRandomBabyJubValue(); 41 | }; 42 | 43 | const bigInt2Buffer = (i) => { 44 | let hexStr = i.toString(16); 45 | while (hexStr.length < 64) { 46 | hexStr = '0' + hexStr; 47 | } 48 | return Buffer.from(hexStr, 'hex'); 49 | }; 50 | 51 | const genPubKey = (privKey) => { 52 | privKey = BigInt(privKey.toString()); 53 | //assert(privKey < STARK_FIELD_SIZE); 54 | return prv2pub(bigInt2Buffer(privKey)); 55 | }; 56 | 57 | const genKeypair = () => { 58 | const privKey = genPrivKey(); 59 | const pubKey = genPubKey(privKey); 60 | 61 | const Keypair = { privKey, pubKey }; 62 | 63 | return Keypair; 64 | }; 65 | 66 | function pruneBuffer(_buff) { 67 | const buff = Buffer.from(_buff); 68 | buff[0] = buff[0] & 0xF8; 69 | buff[31] = buff[31] & 0x7F; 70 | buff[31] = buff[31] | 0x40; 71 | return buff; 72 | } 73 | 74 | const createBlakeHash = require('blake-hash'); 75 | const ff = require('ffjavascript'); 76 | const formatPrivKeyForBabyJub = (privKey) => { 77 | const sBuff = pruneBuffer( 78 | createBlakeHash('blake512').update( 79 | bigInt2Buffer(privKey), 80 | ).digest().slice(0,32), 81 | ); 82 | const s = ff.utils.leBuff2int(sBuff); 83 | return ff.Scalar.shr(s, 3); 84 | }; 85 | 86 | const genEcdhSharedKey = ( 87 | privKey, 88 | pubKey, 89 | ) => { 90 | return mulPointEscalar(pubKey, formatPrivKeyForBabyJub(privKey))[0]; 91 | }; 92 | 93 | const key1 = genKeypair(); 94 | const key2 = genKeypair(); 95 | 96 | const shared_key1 = genEcdhSharedKey(key1.privKey, key2.pubKey); 97 | const shared_key2 = genEcdhSharedKey(key2.privKey, key1.pubKey); 98 | console.log(shared_key1); 99 | console.log(shared_key2); 100 | -------------------------------------------------------------------------------- /cairo/contracts/Getters.cairo: -------------------------------------------------------------------------------- 1 | 2 | @view 3 | func getPublicKey{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(address: felt) -> (a: felt, b: felt): 4 | let (a) = publicKeys.read(address, 0) 5 | let (b) = publicKeys.read(address, 1) 6 | return (a, b) 7 | end 8 | 9 | @view 10 | func getCiphertext{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(token_id: felt) -> (a: felt, b: felt): 11 | let (redeem) = redeemed.read(token_id) 12 | #assert redeem = 1, 'Ciphertext not posted yet' 13 | let (a) = tokenToCiphertext.read(token_id, 0) 14 | let (b) = tokenToCiphertext.read(token_id, 1) 15 | return (a, b) 16 | end 17 | 18 | @view 19 | func getUrl{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(index: felt) -> (url1: felt, url2: felt): 20 | let (url) = urls.read(index) 21 | return (url[0], url[1]) 22 | end 23 | @view 24 | func getUrlData{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(url1: felt, url2: felt) -> (url1: felt, url2: felt, property: felt, price: felt): 25 | let (property) = urlToProperty.read(url1, url2) 26 | let (price) = prices.read(url1, url2) 27 | #let (data: Url) = Url(url=url, property=property, price=price) 28 | return (url1, url2, property, price) 29 | end 30 | @view 31 | func getUrlIndex{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (index: felt): 32 | let (index) = urlIndex.read() 33 | return (index) 34 | end 35 | 36 | @view 37 | func getProperty{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(url1: felt, url2: felt) -> (property: felt): 38 | let (property) = urlToProperty.read(url1, url2) 39 | return (property) 40 | end 41 | 42 | @view 43 | func getPropertyId{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(index: felt) -> (property: felt): 44 | let (property) = _idProperties.read(index) 45 | return (property) 46 | end 47 | 48 | @view 49 | func getNumOfProperties{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (num: felt): 50 | let (num) = numOfProperties.read() 51 | return (num) 52 | end 53 | 54 | @view 55 | func getNumOfTokens{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(url1: felt, url2: felt) -> (num: felt): 56 | let (num) = numOfTokens.read(url1, url2) 57 | return (num) 58 | end 59 | 60 | @view 61 | func getToken{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(url1: felt, url2: felt, index: felt) -> (token: felt): 62 | let (token_id) = _urlTokens.read(url1, url2, index) 63 | return (token=token_id) 64 | end 65 | 66 | @view 67 | func getCreator{storage_ptr: Storage*, pedersen_ptr: HashBuiltin*, range_check_ptr}(url1: felt, url2: felt) -> (address: felt): 68 | let (creator) = _urlCreators.read(url1, url2) 69 | return (address=creator) 70 | end 71 | 72 | -------------------------------------------------------------------------------- /scripts/build_readme.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const circuitName = process.argv[2]; 4 | 5 | const readme = fs.readFileSync('./README.md').toString(); 6 | const sections = readme.split('\n## ').map((_, index) => { 7 | if (index !== 0) { 8 | return `## ${_}`; 9 | } else { 10 | return _; 11 | } 12 | }); 13 | const circuitsSection = sections.filter(_ => { 14 | return _.split('\n')[0] === '## Circuits'; 15 | })[0]; 16 | 17 | const circuit = fs.readFileSync(`./circuits/${circuitName}/circuit.circom`).toString(); 18 | const lines = circuit.split('\n').map(_ => { 19 | let firstChar; 20 | _.split('').forEach((char, index) => { 21 | if (char !== ' ' && typeof firstChar !== 'number') { 22 | firstChar = index; 23 | } 24 | }); 25 | return _.slice(firstChar); 26 | }); 27 | const signals = lines.filter(_ => _.slice(0, 6) === 'signal'); 28 | const inputs = []; 29 | const outputs = []; 30 | signals.forEach(_ => { 31 | const modifiers = _.split(' '); 32 | const variable = modifiers[modifiers.length-1]; 33 | let type; 34 | let name; 35 | if (variable[variable.length-2] === ']') { 36 | const size = variable[variable.length-3]; 37 | type = `Array[${size}]`; 38 | name = variable.slice(0, variable.length-4); 39 | } else { 40 | type = 'BigInt'; 41 | name = variable.slice(0, variable.length-1); 42 | } 43 | switch (modifiers[1]) { 44 | case 'private': 45 | inputs.push({ name, 'private': true, type }); 46 | break; 47 | case 'input': 48 | inputs.push({ name, 'private': false, type }); 49 | break; 50 | case 'output': 51 | return outputs.push({ name, type }); 52 | break; 53 | } 54 | }); 55 | 56 | const header = `#### ${circuitName}`; 57 | const inputHeader = '##### Inputs'; 58 | const inputTableHeader = '| signal | private | type | description |\n|-|-|-|-|'; 59 | const inputTableBody = inputs.map(({ name, private, type }) => { 60 | return `| ${name} | ${private} | ${type} | |`; 61 | }).join('\n'); 62 | const inputTable = `${inputTableHeader}\n${inputTableBody}`; 63 | const outputHeader = '##### Outputs'; 64 | const outputTableHeader = '| signal | type | description |\n|-|-|-|'; 65 | const outputTableBody = outputs.map(({ name, type }) => { 66 | return `| ${name} | ${type} | |`; 67 | }).join('\n'); 68 | const outputTable = `${outputTableHeader}\n${outputTableBody}`; 69 | const inputsSubsection = `${inputHeader}\n${inputTable}`; 70 | const outputsSubsection = `${outputHeader}\n${outputTable}`; 71 | const circuitSubsection = `${header}\n\n${inputsSubsection}\n\n${outputsSubsection}\n\n\n`; 72 | const subsection = `${circuitsSection}\n${circuitSubsection}`; 73 | const newReadme = sections.map(_ => { 74 | if (_.split('\n')[0] === '## Circuits') { 75 | return subsection; 76 | } else { 77 | return _; 78 | } 79 | }).join('\n'); 80 | 81 | fs.writeFileSync('./README.md', newReadme); 82 | -------------------------------------------------------------------------------- /public/fonts/README.txt: -------------------------------------------------------------------------------- 1 | Roboto Mono Variable Font 2 | ========================= 3 | 4 | This download contains Roboto Mono as both variable fonts and static fonts. 5 | 6 | Roboto Mono is a variable font with this axis: 7 | wght 8 | 9 | This means all the styles are contained in these files: 10 | RobotoMono-VariableFont_wght.ttf 11 | RobotoMono-Italic-VariableFont_wght.ttf 12 | 13 | If your app fully supports variable fonts, you can now pick intermediate styles 14 | that aren’t available as static fonts. Not all apps support variable fonts, and 15 | in those cases you can use the static font files for Roboto Mono: 16 | static/RobotoMono-Thin.ttf 17 | static/RobotoMono-ExtraLight.ttf 18 | static/RobotoMono-Light.ttf 19 | static/RobotoMono-Regular.ttf 20 | static/RobotoMono-Medium.ttf 21 | static/RobotoMono-SemiBold.ttf 22 | static/RobotoMono-Bold.ttf 23 | static/RobotoMono-ThinItalic.ttf 24 | static/RobotoMono-ExtraLightItalic.ttf 25 | static/RobotoMono-LightItalic.ttf 26 | static/RobotoMono-Italic.ttf 27 | static/RobotoMono-MediumItalic.ttf 28 | static/RobotoMono-SemiBoldItalic.ttf 29 | static/RobotoMono-BoldItalic.ttf 30 | 31 | Get started 32 | ----------- 33 | 34 | 1. Install the font files you want to use 35 | 36 | 2. Use your app's font picker to view the font family and all the 37 | available styles 38 | 39 | Learn more about variable fonts 40 | ------------------------------- 41 | 42 | https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts 43 | https://variablefonts.typenetwork.com 44 | https://medium.com/variable-fonts 45 | 46 | In desktop apps 47 | 48 | https://theblog.adobe.com/can-variable-fonts-illustrator-cc 49 | https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts 50 | 51 | Online 52 | 53 | https://developers.google.com/fonts/docs/getting_started 54 | https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide 55 | https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts 56 | 57 | Installing fonts 58 | 59 | MacOS: https://support.apple.com/en-us/HT201749 60 | Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux 61 | Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows 62 | 63 | Android Apps 64 | 65 | https://developers.google.com/fonts/docs/android 66 | https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts 67 | 68 | License 69 | ------- 70 | Please read the full license text (LICENSE.txt) to understand the permissions, 71 | restrictions and requirements for usage, redistribution, and modification. 72 | 73 | You can use them freely in your products & projects - print or digital, 74 | commercial or otherwise. However, you can't sell the fonts on their own. 75 | 76 | This isn't legal advice, please consider consulting a lawyer and see the full 77 | license for all details. 78 | -------------------------------------------------------------------------------- /src/components/TextInput.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | type Props = { 5 | id?: string, 6 | placeholder: string, 7 | handleEnter?: (string) => void, 8 | onChange: (value: string) => void, 9 | value: string, 10 | style?: Object, 11 | label?: string, 12 | }; 13 | 14 | const Label = styled.div` 15 | display: flex; 16 | flex: 1; 17 | min-width: 40px; 18 | background-color: ${props => props.theme.color.grey30}; 19 | align-items: center; 20 | justify-content: center; 21 | border-radius: 5px 0px 0px 5px; 22 | border-width: 1px 0px 1px 1px; 23 | border-style: solid; 24 | border-color: ${props => props.theme.color.primary}; 25 | `; 26 | 27 | const TextInputWrapper = styled.input` 28 | font-family: 'Roboto Variable'; 29 | padding: 0 8px; 30 | height: 40px; 31 | width: 100%; 32 | display: flex; 33 | justify-content: center; 34 | align-items: center; 35 | border-radius: 5px; 36 | border-width: 1px; 37 | border-style: solid; 38 | border-color: ${props => props.theme.color.primary}; 39 | font-variation-settings: 'wght' 500; 40 | `; 41 | 42 | const InputDivWrapper = styled.div` 43 | display: flex; 44 | flex-direction: row; 45 | height: 40px; 46 | width: 100%; 47 | margin-bottom: 10px; 48 | `; 49 | 50 | const TextInputWithLabelWrapper = styled.input` 51 | font-family: 'Roboto Variable'; 52 | padding: 0 8px; 53 | display: flex; 54 | justify-content: center; 55 | align-items: center; 56 | border-radius: 0px 5px 5px 0px; 57 | border-width: 1px 1px 1px 0px; 58 | border-style: solid; 59 | border-color: ${props => props.theme.color.primary}; 60 | font-variation-settings: 'wght' 500; 61 | flex: 5; 62 | `; 63 | 64 | 65 | function TextInput (props: Props) { 66 | const detectEnter = (e: any) => { 67 | if (e.key === 'Enter' && props.handleEnter) { 68 | props.handleEnter(e.target.value); 69 | } 70 | }; 71 | 72 | return ( 73 | <> 74 | {props.label ? 75 | 76 | 77 | props.onChange(e.target.value)} 81 | onKeyPress={detectEnter} 82 | placeholder={props.placeholder} 83 | value={props.value} 84 | /> 85 | 86 | : 87 | 88 | props.onChange(e.target.value)} 92 | onKeyPress={detectEnter} 93 | placeholder={props.placeholder} 94 | value={props.value} 95 | /> 96 | 97 | } 98 | 99 | ); 100 | } 101 | 102 | export default TextInput; 103 | -------------------------------------------------------------------------------- /public/contracts/Getters.json: -------------------------------------------------------------------------------- 1 | {"address":"0xc5a5C42992dECbae36851359345FE25997F5C42d","abi":[{"inputs":[{"internalType":"address","name":"_coreContractAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getCiphertext","outputs":[{"internalType":"uint256[2]","name":"","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getContents","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"url","type":"string"},{"internalType":"string","name":"property","type":"string"},{"internalType":"uint256","name":"keyHash","type":"uint256"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"string","name":"zk","type":"string"}],"internalType":"struct ContractStorage.Content[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHashSalt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProperties","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getProperty","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"string","name":"zk","type":"string"}],"name":"getPublicKey","outputs":[{"internalType":"uint256[2]","name":"publicKey","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getToken","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"contentId","type":"uint256"},{"internalType":"uint256","name":"ciphertext1","type":"uint256"},{"internalType":"uint256","name":"ciphertext2","type":"uint256"},{"internalType":"bool","name":"redeemed","type":"bool"}],"internalType":"struct ContractStorage.Token","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"contentId","type":"uint256"}],"name":"getTokens","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"contentId","type":"uint256"},{"internalType":"uint256","name":"ciphertext1","type":"uint256"},{"internalType":"uint256","name":"ciphertext2","type":"uint256"},{"internalType":"bool","name":"redeemed","type":"bool"}],"internalType":"struct ContractStorage.Token[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"}]} -------------------------------------------------------------------------------- /public/contracts/prod/Getters.json: -------------------------------------------------------------------------------- 1 | {"abi":[{"inputs":[{"internalType":"address","name":"_coreContractAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getCiphertext","outputs":[{"internalType":"uint256[2]","name":"","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getContents","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"url","type":"string"},{"internalType":"string","name":"property","type":"string"},{"internalType":"uint256","name":"keyHash","type":"uint256"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"string","name":"zk","type":"string"}],"internalType":"struct ContractStorage.Content[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHashSalt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProperties","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getProperty","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"string","name":"zk","type":"string"}],"name":"getPublicKey","outputs":[{"internalType":"uint256[2]","name":"publicKey","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getToken","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"contentId","type":"uint256"},{"internalType":"uint256","name":"ciphertext1","type":"uint256"},{"internalType":"uint256","name":"ciphertext2","type":"uint256"},{"internalType":"bool","name":"redeemed","type":"bool"}],"internalType":"struct ContractStorage.Token","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"contentId","type":"uint256"}],"name":"getTokens","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"contentId","type":"uint256"},{"internalType":"uint256","name":"ciphertext1","type":"uint256"},{"internalType":"uint256","name":"ciphertext2","type":"uint256"},{"internalType":"bool","name":"redeemed","type":"bool"}],"internalType":"struct ContractStorage.Token[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"}],"address":"0x5251893f3621392e04576a4b7849ebC39ec90d9D"} -------------------------------------------------------------------------------- /src/utils/signature.ts: -------------------------------------------------------------------------------- 1 | import BN from 'bn.js'; 2 | import hash from 'hash.js'; 3 | import { curves as eCurves, ec as EllipticCurve } from 'elliptic'; 4 | import assert from 'assert'; 5 | import constantPointsHex from './constant_points.json'; 6 | 7 | // Equals 2**251 + 17 * 2**192 + 1. 8 | const prime = new BN('800000000000011000000000000000000000000000000000000000000000001', 16); 9 | // Equals 2**251. This value limits msgHash and the signature parts. 10 | const maxEcdsaVal = 11 | new BN('800000000000000000000000000000000000000000000000000000000000000', 16); 12 | 13 | // Generate BN of used constants. 14 | const zeroBn = new BN('0', 16); 15 | const oneBn = new BN('1', 16); 16 | const twoBn = new BN('2', 16); 17 | const twoPow22Bn = new BN('400000', 16); 18 | const twoPow31Bn = new BN('80000000', 16); 19 | const twoPow63Bn = new BN('8000000000000000', 16); 20 | 21 | // Create a curve with stark curve parameters. 22 | const starkEc = new EllipticCurve( 23 | new eCurves.PresetCurve({ 24 | type: 'short', 25 | prime: null, 26 | p: prime, 27 | a: '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001', 28 | b: '06f21413 efbe40de 150e596d 72f7a8c5 609ad26c 15c915c1 f4cdfcb9 9cee9e89', 29 | n: '08000000 00000010 ffffffff ffffffff b781126d cae7b232 1e66a241 adc64d2f', 30 | hash: hash.sha256, 31 | gRed: false, 32 | g: constantPointsHex[1] 33 | }) 34 | ); 35 | 36 | const constantPoints = constantPointsHex.map(coords => ( 37 | starkEc.curve.point(new BN(coords[0], 16), new BN(coords[1], 16)))); 38 | const shiftPoint = constantPoints[0]; 39 | 40 | 41 | const EC_ORDER = 3618502788666131213697322783095070105526743751716087489154079457884512865583; 42 | 43 | function getRandomPrivateKey() { 44 | return BigInt(Math.random() * (EC_ORDER - 1)); 45 | } 46 | 47 | function privateKeyToECPointOnStarkCurve(privateKey) { 48 | return constantPoints[1].mul(new BN(privateKey, 10)); 49 | } 50 | 51 | export function genKeypair() { 52 | const privKey = getRandomPrivateKey(); 53 | const pubKey = privateKeyToECPointOnStarkCurve(privKey); 54 | return { privKey, pubKey }; 55 | } 56 | 57 | export function genSharedKey() { 58 | const key1 = genKeypair(); 59 | const key2 = genKeypair(); 60 | return computeSharedKey(key1.privKey, key2.pubKey); 61 | } 62 | 63 | export function computeSharedKey(privateKey, publicKey) { 64 | const publicKeyPoint = starkEc.curve.point(new BN(publicKey[0], 10), new BN(publicKey[1], 10)) 65 | return publicKeyPoint.mul(new BN(privateKey, 10)).getX().toString(10); 66 | } 67 | 68 | export function pedersen(input) { 69 | let point = shiftPoint; 70 | for (let i = 0; i < input.length; i++) { 71 | let x = new BN(input[i], 16); 72 | assert(x.gte(zeroBn) && x.lt(prime), 'Invalid input: ' + input[i]); 73 | for (let j = 0; j < 252; j++) { 74 | const pt = constantPoints[2 + i * 252 + j]; 75 | assert(!point.getX().eq(pt.getX())); 76 | if (x.and(oneBn).toNumber() !== 0) { 77 | point = point.add(pt); 78 | } 79 | x = x.shrn(1); 80 | } 81 | } 82 | return point.getX().toString(16); 83 | } 84 | -------------------------------------------------------------------------------- /public/circuits/verification_keys/SharedKey.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth16", 3 | "curve": "bn128", 4 | "nPublic": 1, 5 | "vk_alpha_1": [ 6 | "19642524115522290447760970021746675789341356000653265441069630957431566301675", 7 | "15809037446102219312954435152879098683824559980020626143453387822004586242317", 8 | "1" 9 | ], 10 | "vk_beta_2": [ 11 | [ 12 | "3306678135584565297353192801602995509515651571902196852074598261262327790404", 13 | "6402738102853475583969787773506197858266321704623454181848954418090577674938" 14 | ], 15 | [ 16 | "4983765881427969364617654516554524254158908221590807345159959200407712579883", 17 | "15158588411628049902562758796812667714664232742372443470614751812018801551665" 18 | ], 19 | [ 20 | "1", 21 | "0" 22 | ] 23 | ], 24 | "vk_gamma_2": [ 25 | [ 26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781", 27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634" 28 | ], 29 | [ 30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930", 31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531" 32 | ], 33 | [ 34 | "1", 35 | "0" 36 | ] 37 | ], 38 | "vk_delta_2": [ 39 | [ 40 | "2528491300866434509699704412642731178102268865012248785813458721505586631446", 41 | "11078114351411396302492606863995638386506537365844689646898417550998267219414" 42 | ], 43 | [ 44 | "17448560587075395769884970409122010185777125947946128673908172602768905142360", 45 | "7646900014588577959937375249841784560277351960820231527167492175864420231155" 46 | ], 47 | [ 48 | "1", 49 | "0" 50 | ] 51 | ], 52 | "vk_alphabeta_12": [ 53 | [ 54 | [ 55 | "5990981426838611487817331801723298853689232371402636505882752025826717861263", 56 | "6471600557194756298005957410208198947034348214534657394165503369790309076215" 57 | ], 58 | [ 59 | "15002700749801870140343913307313851505416165248926784119266834715141460279528", 60 | "3830910611342760800353177932876565223705290639657811356514431363408808083245" 61 | ], 62 | [ 63 | "16213272029928492311641815345584648151738099578429924053941653329679116056011", 64 | "11521059852145391195901894023171250860281638548552692281491114389569775909273" 65 | ] 66 | ], 67 | [ 68 | [ 69 | "21473208233673181545713372461262158409524966660539444635240255266528451743613", 70 | "12831374732506919217314780439582796881729424911714976211545279975915712437524" 71 | ], 72 | [ 73 | "6384272187442204077342066657702496903903539015888454871400719146512483196930", 74 | "11896033867460826226898768849934024947887432723445089197160889467704536005586" 75 | ], 76 | [ 77 | "4320872437228236846208766625647342285071164667482133950438695528545836489222", 78 | "13210525034366360416090582915961576197528924758377030567364985602065069567971" 79 | ] 80 | ] 81 | ], 82 | "IC": [ 83 | [ 84 | "16463174501805780021089853501376060963515156895898693392073063915686087047275", 85 | "13414674582095658163460475026964050547254898204628732893702194359321219522701", 86 | "1" 87 | ], 88 | [ 89 | "13266294820635328754624242943449864774638145789973957978478542841176910167529", 90 | "3018470127095380838034414633037279244203248256028953869896205416849246480481", 91 | "1" 92 | ] 93 | ] 94 | } -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-waffle"; 2 | import "@typechain/hardhat"; 3 | import "hardhat-gas-reporter"; 4 | import "solidity-coverage"; 5 | import "@nomiclabs/hardhat-ethers"; 6 | import "@nomiclabs/hardhat-etherscan"; 7 | 8 | import { config as dotenvConfig } from "dotenv"; 9 | import { HardhatUserConfig } from "hardhat/config"; 10 | import { NetworkUserConfig } from "hardhat/types"; 11 | import { resolve } from "path"; 12 | 13 | import "./scripts/deploy"; 14 | 15 | dotenvConfig({ path: resolve(__dirname, "./.env") }); 16 | 17 | const chainIds = { 18 | goerli: 5, 19 | hardhat: 31337, 20 | kovan: 42, 21 | mainnet: 1, 22 | rinkeby: 4, 23 | ropsten: 3, 24 | }; 25 | 26 | // Ensure that we have all the environment variables we need. 27 | const mnemonic: string | undefined = process.env.MNEMONIC; 28 | if (!mnemonic) { 29 | throw new Error("Please set your MNEMONIC in a .env file"); 30 | } 31 | 32 | const infuraApiKey: string | undefined = process.env.INFURA_API_KEY; 33 | if (!infuraApiKey) { 34 | throw new Error("Please set your INFURA_API_KEY in a .env file"); 35 | } 36 | 37 | function getChainConfig(network: keyof typeof chainIds): NetworkUserConfig { 38 | const url: string = `https://${network}.infura.io/v3/${infuraApiKey}`; 39 | return { 40 | accounts: { 41 | count: 10, 42 | mnemonic, 43 | path: "m/44'/60'/0'/0", 44 | }, 45 | chainId: chainIds[network], 46 | url, 47 | }; 48 | } 49 | 50 | const config: HardhatUserConfig = { 51 | defaultNetwork: "hardhat", 52 | networks: { 53 | // hardhat: { 54 | // accounts: { 55 | // mnemonic, 56 | // }, 57 | // chainId: chainIds.hardhat, 58 | // }, 59 | goerli: getChainConfig("goerli"), 60 | ropsten: getChainConfig("ropsten"), 61 | localhost: { 62 | url: "http://127.0.0.1:8545", 63 | }, 64 | }, 65 | paths: { 66 | artifacts: "./artifacts", 67 | cache: "./cache", 68 | sources: "./contracts", 69 | tests: "./test", 70 | }, 71 | solidity: { 72 | compilers: [ 73 | { 74 | version: "0.8.3", 75 | settings: { 76 | metadata: { 77 | // Not including the metadata hash 78 | // https://github.com/paulrberg/solidity-template/issues/31 79 | bytecodeHash: "none", 80 | }, 81 | // Disable the optimizer when debugging 82 | // https://hardhat.org/hardhat-network/#solidity-optimizer-support 83 | optimizer: { 84 | enabled: true, 85 | runs: 800, 86 | }, 87 | }, 88 | }, 89 | { 90 | version: "0.6.11", 91 | settings: { 92 | metadata: { 93 | // Not including the metadata hash 94 | // https://github.com/paulrberg/solidity-template/issues/31 95 | bytecodeHash: "none", 96 | }, 97 | // Disable the optimizer when debugging 98 | // https://hardhat.org/hardhat-network/#solidity-optimizer-support 99 | optimizer: { 100 | enabled: true, 101 | runs: 800, 102 | }, 103 | }, 104 | }, 105 | ], 106 | }, 107 | /* 108 | typechain: { 109 | outDir: "typechain", 110 | target: "ethers-v5", 111 | }, 112 | */ 113 | etherscan: { 114 | apiKey: process.env.ETHERSCAN_API_KEY, 115 | }, 116 | }; 117 | 118 | export default config; 119 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 5 | const CopyPlugin = require('copy-webpack-plugin'); 6 | 7 | //const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 8 | 9 | const createStyledComponentsTransformer = require('typescript-plugin-styled-components') 10 | .default; 11 | const styledComponentsTransformer = createStyledComponentsTransformer(); 12 | 13 | module.exports = { 14 | mode: 'production', 15 | entry: { 16 | 'bundle.js': __dirname + '/src/index.tsx', 17 | }, 18 | output: { 19 | path: path.join(__dirname, 'dist'), 20 | filename: 'bundle.min.js', 21 | publicPath: '/', 22 | }, 23 | 24 | // Enable sourcemaps for debugging webpack's output. 25 | devtool: 'source-map', 26 | devServer: { 27 | port: 5000, 28 | historyApiFallback: true, 29 | publicPath: '/', 30 | }, 31 | 32 | resolve: { 33 | // Add '.ts' and '.tsx' as resolvable extensions. 34 | extensions: ['.ts', '.tsx', '.js', '.jsx'], 35 | // we need this to reference files in the symlinked src/circuits directory 36 | symlinks: false, 37 | }, 38 | 39 | module: { 40 | rules: [ 41 | { 42 | test: /\.ts(x?)$/, 43 | exclude: [ 44 | /node_modules/, 45 | /snarkjs.min.js/ 46 | ], 47 | loader: 'ts-loader', 48 | options: { 49 | getCustomTransformers: () => ({ 50 | before: [styledComponentsTransformer], 51 | }), 52 | }, 53 | }, 54 | { 55 | test: /\.css$/, 56 | use: ['style-loader', 'css-loader'], 57 | }, 58 | { 59 | test: /\.(woff(2)?|ttf|eot|svg)$/, 60 | use: [ 61 | { 62 | loader: 'file-loader', 63 | options: { 64 | name: '[name].[ext]', 65 | outputPath: 'fonts/' 66 | } 67 | } 68 | ] 69 | }, 70 | { 71 | test: /snarkjs.min.js/, 72 | use: [ 73 | { 74 | loader: 'file-loader', 75 | } 76 | ] 77 | }, 78 | { 79 | test: /\.(js|jsx)$/, 80 | exclude: [ 81 | /node_modules/, 82 | /snarkjs.min.js/ 83 | ], 84 | use: { 85 | loader: "babel-loader", 86 | options: { 87 | presets: ["@babel/preset-env"] 88 | } 89 | } 90 | }, 91 | { 92 | enforce: 'pre', 93 | test: /\.js$/, 94 | exclude: [ 95 | /@ethersproject/ 96 | ], 97 | loader: 'source-map-loader', 98 | }, 99 | ], 100 | }, 101 | plugins: [ 102 | new CleanWebpackPlugin(), 103 | new CopyPlugin({ 104 | patterns: [ 105 | { from: 'public', to: '' }, 106 | { from: 'circuits', to: 'circuits' } 107 | ] 108 | }), 109 | ], 110 | 111 | // When importing a module whose path matches one of the following, just 112 | // assume a corresponding global variable exists and use that instead. 113 | // This is important because it allows us to avoid bundling all of our 114 | // dependencies, which allows browsers to cache those libraries between builds. 115 | externals: { 116 | }, 117 | node: { 118 | fs: "empty", 119 | tls: "empty" 120 | } 121 | }; 122 | -------------------------------------------------------------------------------- /src/app/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useCallback } from 'react'; 2 | import { HashRouter, Switch } from 'react-router-dom'; 3 | import Web3Modal from 'web3modal'; 4 | import WalletConnectProvider from '@walletconnect/web3-provider'; 5 | import { providers } from 'ethers'; 6 | 7 | import config from '../../config'; 8 | 9 | import Page from './Page'; 10 | 11 | import NewToken from '../pages/NewToken'; 12 | import Token from '../pages/Token'; 13 | import Tokens from '../pages/Tokens'; 14 | import ChooseUser from '../pages/ChooseUser'; 15 | 16 | import eth from '../utils/ethAPI'; 17 | 18 | const INFURA_ID = config.infuraId; 19 | const chain = config.chain; 20 | 21 | const web3Modal = new Web3Modal({ 22 | network: chain, 23 | cacheProvider: true, // optional 24 | theme: 'light', // optional. Change to "dark" for a dark theme. 25 | providerOptions: { 26 | }, 27 | }); 28 | 29 | const App = () => { 30 | const [signer, setSigner] = useState(); 31 | 32 | const logoutOfWeb3Modal = async () => { 33 | await web3Modal.clearCachedProvider(); 34 | if (eth.provider && 35 | eth.provider['provider'] && 36 | typeof eth.provider['provider'].disconnect == 'function') { 37 | await eth.provider['provider'].disconnect(); 38 | } 39 | setTimeout(() => { 40 | window.location.reload(); 41 | }, 1); 42 | }; 43 | 44 | const loadWeb3Modal = useCallback(async () => { 45 | const provider = await web3Modal.connect(); 46 | const injectedProvider = new providers.Web3Provider(provider); 47 | eth.setProvider(injectedProvider, setSigner); 48 | 49 | provider.on('chainChanged', chainId => { 50 | console.log(`chain changed to ${chainId}! updating providers`); 51 | }); 52 | 53 | provider.on('accountsChanged', () => { 54 | console.log('account changed!'); 55 | eth.setProvider(injectedProvider, setSigner); 56 | }); 57 | 58 | provider.on('disconnect', (code, reason) => { 59 | console.log(code, reason); 60 | logoutOfWeb3Modal(); 61 | }); 62 | }, []); 63 | 64 | useEffect(() => { 65 | const { env } = config; 66 | let provider; 67 | if (env === 'production') { 68 | loadWeb3Modal(); 69 | } else { 70 | const network_url = 'http://localhost:8545'; 71 | provider = new providers.JsonRpcProvider(network_url); 72 | eth.setProvider(provider, setSigner); 73 | } 74 | }, []); 75 | 76 | return ( 77 | 78 | 79 | 86 | 93 | 100 | 107 | 114 | 115 | 116 | ); 117 | }; 118 | 119 | export default App; 120 | -------------------------------------------------------------------------------- /contracts/Getters.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.7.6; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./Core.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; 6 | import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 7 | import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; 8 | 9 | 10 | interface ICore { 11 | function getPublicKey(address _address, string calldata zk) view external returns (uint256[2] memory publicKey); 12 | 13 | function getContent(uint256 contentId) view external returns (ContractStorage.Content memory); 14 | 15 | function contentCount() view external returns (uint256); 16 | 17 | function getToken(uint id) view external returns (ContractStorage.Token memory); 18 | 19 | function getTokenIds(uint256 contentId) view external returns (uint256[] memory); 20 | 21 | function getNumOfProperties() view external returns (uint256 length); 22 | 23 | function getProperty(uint256 id) view external returns (string memory); 24 | } 25 | 26 | contract Getters { 27 | 28 | ICore coreContract; 29 | 30 | constructor( 31 | address _coreContractAddress 32 | ) public { 33 | coreContract = ICore(_coreContractAddress); 34 | } 35 | 36 | function getPublicKey(address _address, string calldata zk) public view returns (uint256[2] memory publicKey) { 37 | return coreContract.getPublicKey(_address, zk); 38 | } 39 | 40 | function getContents() public view returns (ContractStorage.Content[] memory) { 41 | ContractStorage.Content[] memory ret = 42 | new ContractStorage.Content[](coreContract.contentCount()); 43 | 44 | for (uint256 i = 0; i < coreContract.contentCount(); i++) { 45 | ret[i] = coreContract.getContent(i); 46 | } 47 | 48 | return ret; 49 | } 50 | 51 | function getCreator(uint256 id) public view returns (address) { 52 | return coreContract.getContent(id).creator; 53 | } 54 | 55 | function getCiphertext(uint256 tokenId) public view returns (uint256[2] memory) { 56 | ContractStorage.Token memory token = coreContract.getToken(tokenId); 57 | require(token.redeemed == true, 'DataMarketplaceCore/ciphertext-not-posted'); 58 | uint256[2] memory ciphertext; 59 | ciphertext[0] = token.ciphertext1; 60 | ciphertext[1] = token.ciphertext2; 61 | return ciphertext; 62 | } 63 | 64 | function getProperty(uint256 id) public view returns (string memory) { 65 | return coreContract.getContent(id).property; 66 | } 67 | 68 | function getProperties() public view returns (string[] memory) { 69 | uint256 length = coreContract.getNumOfProperties(); 70 | string[] memory _properties = new string[](length); 71 | for (uint i=1; i<=length; i++) { 72 | _properties[i-1] = coreContract.getProperty(i); 73 | } 74 | return _properties; 75 | } 76 | 77 | function getToken(uint256 tokenId) public view returns (ContractStorage.Token memory) { 78 | return coreContract.getToken(tokenId); 79 | } 80 | 81 | function getTokens(uint256 contentId) public view returns (ContractStorage.Token[] memory) { 82 | uint256[] memory tokenIds = coreContract.getTokenIds(contentId); 83 | uint tokenCount = tokenIds.length; 84 | ContractStorage.Token[] memory ret = 85 | new ContractStorage.Token[](tokenCount); 86 | 87 | for (uint256 i = 0; i < tokenCount; i++) { 88 | ret[i] = coreContract.getToken(tokenIds[i]); 89 | } 90 | 91 | return ret; 92 | } 93 | 94 | function getHashSalt() public view returns (uint256) { 95 | // DarkForestCore.SnarkConstants memory s = darkForestCore.snarkConstants(); 96 | return 0; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/pages/content/Hash.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import styled from 'styled-components'; 3 | import assert from 'assert'; 4 | 5 | import TextInput from '../../components/TextInput'; 6 | import { Large } from '../../components/text'; 7 | import Resize from '../../components/Resize'; 8 | 9 | import { 10 | ZKFunctions, 11 | stringToNum, 12 | } from '../../utils/crypto'; 13 | 14 | import { InputProps } from '../../types/content'; 15 | import { ZKTypes } from '../../types'; 16 | 17 | 18 | const HashWrapper = styled.div` 19 | padding: 20px; 20 | width: 100%; 21 | box-sizing: border-box; 22 | `; 23 | 24 | const HashText = styled(Large)` 25 | font-size: 18px; 26 | `; 27 | 28 | type HashProps = { 29 | property: BigInt; 30 | message?: string; 31 | } 32 | 33 | const Hash = (props: HashProps) => { 34 | const hashString = props.property.toString(); 35 | const [display, setDisplay] = useState(null); 36 | const [containerRef, setContainerRef] = useState(null); 37 | const [contentRef, setContentRef] = useState(null); 38 | 39 | useEffect(() => { 40 | const _display = props.message ? props.message : hashString; 41 | setDisplay(_display); 42 | }, [props]); 43 | 44 | const sliceFunction = (size) => { 45 | const hashArray = hashString.split(''); 46 | 47 | const halfSize = Math.floor((size-3) / 2); 48 | const hashFirstHalf = hashArray.slice(0, halfSize).join(''); 49 | const hashLastHalf = hashArray.slice(hashArray.length - halfSize).join(''); 50 | return `${hashFirstHalf}...${hashLastHalf}`; 51 | }; 52 | 53 | return ( 54 | 60 | 61 | {display} 62 | 63 | 64 | ); 65 | }; 66 | 67 | const HashInput = (props: InputProps) => { 68 | 69 | useEffect(() => { 70 | props.setPreimage(''); 71 | }, []); 72 | 73 | return ( 74 | 79 | ); 80 | }; 81 | 82 | const isCiphertext = (value) => { 83 | return typeof value.iv === 'bigint' && 84 | (Array.isArray(value.data) && typeof value.data[0] === 'bigint'); 85 | }; 86 | 87 | const assertContent = (content) => { 88 | assert(isCiphertext(content.cipher)); 89 | assert(typeof content.property === 'bigint'); 90 | }; 91 | 92 | const assertMessage = (message) => assert(typeof message === 'string'); 93 | 94 | const computeProperty = (preimage): any[] => { 95 | const numPreimage = stringToNum(preimage); 96 | const salt = BigInt('0'); 97 | return [numPreimage, salt]; 98 | }; 99 | 100 | const assertProofInputs = (args: any[]) => { 101 | assert(args.length === 3); 102 | assert(typeof args[0] === 'bigint'); 103 | assert(typeof args[1] === 'bigint'); 104 | assert(typeof args[2] === 'bigint'); 105 | }; 106 | 107 | const json = { 108 | input: HashInput, 109 | display: Hash, 110 | list: Hash, 111 | decrypt: (zk, ...args) => 112 | ZKFunctions[zk].decryptMessageCiphertext(...args), 113 | computeProperty, 114 | prover: { 115 | [ZKTypes.SNARK]: ZKFunctions[ZKTypes.SNARK].provers.hash, 116 | [ZKTypes.STARK]: ZKFunctions[ZKTypes.STARK].provers.hash, 117 | }, 118 | verifier: { 119 | [ZKTypes.SNARK]: ZKFunctions[ZKTypes.SNARK].verifiers.hash, 120 | [ZKTypes.STARK]: ZKFunctions[ZKTypes.STARK].verifiers.hash, 121 | }, 122 | assertProofInputs, 123 | assertContent, 124 | assertMessage, 125 | }; 126 | export default json; 127 | -------------------------------------------------------------------------------- /src/pages/Tokens.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { Button } from '../components/Button'; 5 | import { Header } from '../components/text'; 6 | 7 | import PropertyToggle from '../app/PropertyToggle'; 8 | import { ContentElements } from './Content'; 9 | 10 | import eth from '../utils/ethAPI'; 11 | import ipfs from '../utils/ipfs'; 12 | import { Parsers } from '../utils/parsers'; 13 | import { 14 | ContentProperties, 15 | Snark, 16 | Stark, 17 | ZKTypes, 18 | } from '../types'; 19 | 20 | const TokensWrapper = styled.div` 21 | display: flex; 22 | flex-direction: column; 23 | align-items: center; 24 | margin-top: 10%; 25 | margin-bottom: 10px; 26 | margin-left: 15%; 27 | margin-right: 15%; 28 | `; 29 | 30 | const TokenWrapper = styled.div` 31 | display: flex; 32 | flex-direction: row; 33 | width: calc(452px - 22px); 34 | height: ${props => props.theme.spacing(6)}; 35 | background-color: ${props => props.theme.color.white}; 36 | border: ${props => `1px solid ${props.theme.color.grey30}`}; 37 | margin-bottom: 10px; 38 | border-radius: 4px; 39 | padding: 11px; 40 | cursor: pointer; 41 | align-items: center; 42 | :hover { 43 | background-color: ${props => props.theme.color.grey10}; 44 | } 45 | `; 46 | 47 | const PostsWrapper = styled.div` 48 | display: flex; 49 | flex-direction: column; 50 | flex: 1; 51 | width: calc(100% - 100px); 52 | padding-left: 50px; 53 | padding-right: 50px; 54 | padding-top: 10px; 55 | align-items: center; 56 | `; 57 | 58 | type TokenProps = { 59 | url: string; 60 | zk: ZKTypes; 61 | property: ContentProperties; 62 | onClick: () => void; 63 | }; 64 | 65 | const Token = (props: TokenProps) => { 66 | const [property, setProperty] = useState(null); 67 | const [loading, setLoading] = useState(true); 68 | 69 | useEffect(() => { 70 | ipfs.getProof(props.url, props.zk).then(proof => { 71 | // @ts-ignore 72 | const { contentProperty } = Parsers[props.zk][props.property](proof); 73 | setProperty(contentProperty); 74 | setLoading(false); 75 | }); 76 | }, []); 77 | 78 | const PropertyListElement = ContentElements[props.property].list; 79 | 80 | return ( 81 | 82 | {!loading ? 83 | 84 | : null} 85 | 86 | ); 87 | }; 88 | 89 | const Tokens = (props) => { 90 | const [tokens, setTokens] = useState([]); 91 | const [property, setProperty] = 92 | useState(ContentProperties.HASH); 93 | 94 | useEffect(() => { 95 | if (props.signer) { 96 | eth.api.getContents().then(contents => { 97 | setTokens(contents); 98 | }); 99 | } 100 | }, [props.signer, property]); 101 | 102 | const sendToToken = (contentId) => () => { 103 | props.history.push(`/tokens/${contentId}`); 104 | }; 105 | 106 | const sendToNewToken = () => { 107 | props.history.push('/tokens/new'); 108 | }; 109 | 110 | return ( 111 | <> 112 | 113 |
Discover Tokens
114 | 115 | 118 |
119 | 120 | {tokens.filter(_ => _.property === property).map(_ => )} 127 | 128 | 129 | ); 130 | }; 131 | 132 | export default Tokens; 133 | -------------------------------------------------------------------------------- /src/pages/NewToken.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { Button } from '../components/Button'; 5 | import { Large } from '../components/text'; 6 | import Spinner from '../components/Spinner'; 7 | import TextInput from '../components/TextInput'; 8 | import Toggle from '../components/Toggle'; 9 | import PropertyToggle from '../app/PropertyToggle'; 10 | import { ContentInput, ContentElements } from './Content'; 11 | import { 12 | setKey, 13 | ZKFunctions, 14 | } from '../utils/crypto'; 15 | import eth from '../utils/ethAPI'; 16 | import ipfs from '../utils/ipfs'; 17 | 18 | import { 19 | ContentProperties, 20 | ZKTypes, 21 | IpfsResponse, 22 | Snark, 23 | Stark, 24 | } from '../types'; 25 | 26 | 27 | const Title = styled(Large)` 28 | margin-bottom: 10px; 29 | `; 30 | 31 | const NewTokenWrapper = styled.div` 32 | display: flex; 33 | flex-direction: column; 34 | align-items: center; 35 | margin-left: 20%; 36 | margin-right: 20%; 37 | margin-top: 10%; 38 | height: 100%; 39 | `; 40 | 41 | const NewToken = (props) => { 42 | const [preimage, setPreimage] = useState(''); 43 | const [property, setProperty] = 44 | useState(ContentProperties.HASH); 45 | const [zk, setZK] = 46 | useState(ZKTypes.SNARK); 47 | const [price, setPrice] = useState(BigInt(0)); 48 | const [loading, setLoading] = useState(false); 49 | const [loadingMessage, setLoadingMessage] = useState(''); 50 | 51 | const sendToTokens = () => { 52 | props.history.push('/tokens'); 53 | }; 54 | 55 | const onSell = async () => { 56 | setLoading(true); 57 | 58 | const commitProof = (proof: Snark | Stark) => { 59 | ipfs.addProof(proof, zk).then((result: IpfsResponse) => { 60 | let url = result.path; 61 | 62 | setKey(url, sharedKey); 63 | 64 | eth.api.postUrl(url, keyHash, property, price, zk) 65 | .then(() => { 66 | sendToTokens(); 67 | setLoading(false); 68 | }) 69 | .catch(error => { 70 | setLoading(false); 71 | alert(error.message); 72 | }); 73 | localStorage.setItem(url, preimage); 74 | }); 75 | }; 76 | 77 | const sharedKey = await ZKFunctions[zk].genSharedKey(); 78 | const keyHash = await ZKFunctions[zk].hash(sharedKey, BigInt(0)); 79 | 80 | setLoadingMessage('generating proof'); 81 | const { 82 | prover, 83 | computeProperty, 84 | assertProofInputs, 85 | } = ContentElements[property]; 86 | const values = await computeProperty(preimage, sharedKey); 87 | const proofInputs = [sharedKey, ...values]; 88 | assertProofInputs(proofInputs); 89 | prover[zk](proofInputs).then(commitProof); 90 | }; 91 | 92 | const setPriceBigInt = (value: string) => { 93 | if (parseInt(value)) { 94 | setPrice(BigInt(value)); 95 | } 96 | }; 97 | 98 | return ( 99 | <> 100 | {loading ? 101 | 102 | : 103 | 104 | Sell your Token 105 | 106 | 111 | 116 | 122 | 125 | 126 | } 127 | 128 | ); 129 | }; 130 | 131 | export default NewToken; 132 | -------------------------------------------------------------------------------- /src/app/NavigationBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import styled from 'styled-components'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | import { Button } from '../components/Button'; 6 | import { Large } from '../components/text'; 7 | import Ipfs from './Ipfs'; 8 | import { selfTheme as theme } from '../styles/theme'; 9 | 10 | 11 | interface NavBarElementProps { 12 | active: boolean; 13 | } 14 | 15 | const color = theme.color.darkText; 16 | const NavBarElementWrapper = styled.div` 17 | border-bottom: ${props => (props.active ? 18 | `3px solid ${color}` : 'none')}; 19 | :hover { 20 | border-bottom: ${() => `3px solid ${color}`}; 21 | div { 22 | color: ${color}; 23 | } 24 | } 25 | display: flex; 26 | align-items: center; 27 | div { 28 | color: ${props => (props.active ? color : 'default')}; 29 | } 30 | `; 31 | 32 | type NavigationBarElementProps = { 33 | path: string; 34 | title: string; 35 | icon?: string; 36 | activeTab: string; 37 | }; 38 | 39 | const NavigationBarElement = (props: NavigationBarElementProps) => { 40 | return ( 41 | 42 | 43 | {props.title} 44 | 45 | 46 | ); 47 | }; 48 | 49 | const NavigationBarWrapper = styled.div` 50 | width: 100%; 51 | height: 60px; 52 | display: flex; 53 | align-items: center; 54 | flex-direction: row; 55 | background-color: ${props => props.theme.color.white}; 56 | border-bottom: ${props => `1px solid ${props.theme.color.grey30}`}; 57 | a { 58 | text-decoration: none; 59 | } 60 | position: fixed; 61 | `; 62 | 63 | const ElementsWrapper = styled.div` 64 | flex: 2; 65 | height: 100%; 66 | display: flex; 67 | flex-direction: row; 68 | justify-content: space-around; 69 | `; 70 | 71 | const SidebarWrapper = styled.div` 72 | display: flex; 73 | justify-content: space-around; 74 | flex: 1; 75 | align-items: center; 76 | `; 77 | 78 | const LogoutButton = styled(Button)` 79 | display: flex; 80 | margin-right: 10px; 81 | padding: 10px; 82 | flex: 1; 83 | `; 84 | 85 | const Title = styled(Large)` 86 | margin-right: 20px; 87 | color: ${color}; 88 | `; 89 | 90 | type NavigationBarProps = { 91 | activeTab: string; 92 | history: any; 93 | signer: any; 94 | web3: any; 95 | } 96 | 97 | const NavigationBar = (props: NavigationBarProps) => { 98 | const [address, setAddress] = useState(''); 99 | const [addresses, setAddresses] = useState([]); 100 | 101 | useEffect(() => { 102 | if (props.signer) { 103 | props.signer.getAddress().then(add => { 104 | setAddress(add); 105 | }); 106 | props.signer.provider.listAccounts().then(setAddresses); 107 | } 108 | }, [props.signer]); 109 | 110 | const sendToChooseUser = () => { 111 | props.history.push('/choose-user'); 112 | }; 113 | 114 | return ( 115 | 116 | 117 | 122 | 123 | {addresses.length > 1 ? 124 | 125 | Choose Address 126 | 127 | : null} 128 | {address ? 129 | 130 | {address.slice(0,6)} 131 | 132 | 133 | Logout 134 | 135 | 136 | : 137 | 138 | 139 | {}}> 140 | Login 141 | 142 | 143 | } 144 | 145 | ); 146 | }; 147 | 148 | export default NavigationBar; 149 | -------------------------------------------------------------------------------- /crypto/babyjub.js: -------------------------------------------------------------------------------- 1 | const F1Field = require('ffjavascript').F1Field; 2 | const Scalar = require('ffjavascript').Scalar; 3 | const utils = require('ffjavascript').utils; 4 | 5 | exports.addPoint = addPoint; 6 | exports.mulPointEscalar = mulPointEscalar; 7 | exports.inCurve = inCurve; 8 | exports.inSubgroup = inSubgroup; 9 | exports.packPoint = packPoint; 10 | exports.unpackPoint = unpackPoint; 11 | 12 | 13 | exports.p = Scalar.fromString('3618502788666131213697322783095070105623107215331596699973092056135872020481'); 14 | const F = new F1Field(exports.p); 15 | exports.F = F; 16 | 17 | exports.Generator = [ 18 | F.e('874739451078007766457464989774322083649278607533249481151382481072868806602'), 19 | F.e('152666792071518830868575557812948353041420400780739481342941381225525861407') 20 | ]; 21 | 22 | exports.beta = F.e('3141592653589793238462643383279502884197169399375105820974944592307816406665'); 23 | exports.Base8 = [ 24 | F.e('5299619240641551281634865583518297030282874472190772894086521144482721001553'), 25 | F.e('16950150798460657717958625567821834550301663161624707787222815936182638968203') 26 | ]; 27 | exports.order = Scalar.fromString('3618502788666131213697322783095070105623107215331596699973092056135872020481'); 28 | exports.subOrder = Scalar.shiftRight(exports.order, 3); 29 | exports.A = F.e('168700'); 30 | exports.D = F.e('168696'); 31 | 32 | 33 | function addPoint(a,b) { 34 | 35 | const res = []; 36 | 37 | /* does the equivalent of: 38 | res[0] = bigInt((a[0]*b[1] + b[0]*a[1]) * bigInt(bigInt('1') + d*a[0]*b[0]*a[1]*b[1]).inverse(q)).affine(q); 39 | res[1] = bigInt((a[1]*b[1] - cta*a[0]*b[0]) * bigInt(bigInt('1') - d*a[0]*b[0]*a[1]*b[1]).inverse(q)).affine(q); 40 | */ 41 | 42 | const beta = F.mul(a[0],b[1]); 43 | const gamma = F.mul(a[1],b[0]); 44 | const delta = F.mul( 45 | F.sub(a[1], F.mul(exports.A, a[0])), 46 | F.add(b[0], b[1]) 47 | ); 48 | const tau = F.mul(beta, gamma); 49 | const dtau = F.mul(exports.D, tau); 50 | 51 | res[0] = F.div( 52 | F.add(beta, gamma), 53 | F.add(F.one, dtau) 54 | ); 55 | 56 | res[1] = F.div( 57 | F.add(delta, F.sub(F.mul(exports.A,beta), gamma)), 58 | F.sub(F.one, dtau) 59 | ); 60 | 61 | return res; 62 | } 63 | 64 | function mulPointEscalar(base, e) { 65 | let res = [F.e('0'),F.e('1')]; 66 | let rem = e; 67 | let exp = base; 68 | 69 | while (! Scalar.isZero(rem)) { 70 | if (Scalar.isOdd(rem)) { 71 | res = addPoint(res, exp); 72 | } 73 | exp = addPoint(exp, exp); 74 | rem = Scalar.shiftRight(rem, 1); 75 | } 76 | 77 | return res; 78 | } 79 | 80 | function inSubgroup(P) { 81 | if (!inCurve(P)) return false; 82 | const res= mulPointEscalar(P, exports.subOrder); 83 | return (F.isZero(res[0]) && F.eq(res[1], F.one)); 84 | } 85 | 86 | function inCurve(P) { 87 | 88 | const x2 = F.square(P[0]); 89 | const y2 = F.square(P[1]); 90 | 91 | if (!F.eq( 92 | F.add(F.mul(exports.A, x2), y2), 93 | F.add(F.one, F.mul(F.mul(x2, y2), exports.D)))) return false; 94 | 95 | return true; 96 | } 97 | 98 | function packPoint(P) { 99 | const buff = utils.leInt2Buff(P[1], 32); 100 | if (F.lt(P[0], F.zero)) { 101 | buff[31] = buff[31] | 0x80; 102 | } 103 | return buff; 104 | } 105 | 106 | function unpackPoint(_buff) { 107 | const buff = Buffer.from(_buff); 108 | let sign = false; 109 | const P = new Array(2); 110 | if (buff[31] & 0x80) { 111 | sign = true; 112 | buff[31] = buff[31] & 0x7F; 113 | } 114 | P[1] = utils.leBuff2int(buff); 115 | if (Scalar.gt(P[1], exports.p)) return null; 116 | 117 | const y2 = F.square(P[1]); 118 | 119 | let x = F.sqrt(F.div( 120 | F.sub(F.one, y2), 121 | F.sub(exports.A, F.mul(exports.D, y2)))); 122 | 123 | if (x == null) return null; 124 | 125 | if (sign) x = F.neg(x); 126 | 127 | P[0] = x; 128 | 129 | return P; 130 | } 131 | -------------------------------------------------------------------------------- /artifacts/contracts/DarkForestCore.sol/DarkForestCore.json: -------------------------------------------------------------------------------- 1 | { 2 | "_format": "hh-sol-artifact-1", 3 | "contractName": "DarkForestCore", 4 | "sourceName": "contracts/DarkForestCore.sol", 5 | "abi": [ 6 | { 7 | "inputs": [], 8 | "stateMutability": "nonpayable", 9 | "type": "constructor" 10 | }, 11 | { 12 | "inputs": [ 13 | { 14 | "internalType": "uint256", 15 | "name": "key", 16 | "type": "uint256" 17 | } 18 | ], 19 | "name": "planets", 20 | "outputs": [ 21 | { 22 | "components": [ 23 | { 24 | "internalType": "address", 25 | "name": "owner", 26 | "type": "address" 27 | }, 28 | { 29 | "internalType": "bool", 30 | "name": "isHomePlanet", 31 | "type": "bool" 32 | } 33 | ], 34 | "internalType": "struct DarkForestUtils.Planet", 35 | "name": "", 36 | "type": "tuple" 37 | } 38 | ], 39 | "stateMutability": "view", 40 | "type": "function" 41 | }, 42 | { 43 | "inputs": [], 44 | "name": "s", 45 | "outputs": [ 46 | { 47 | "components": [ 48 | { 49 | "internalType": "uint256", 50 | "name": "PLANETHASH_KEY", 51 | "type": "uint256" 52 | } 53 | ], 54 | "internalType": "struct DarkForestUtils.SnarkConstants", 55 | "name": "snarkConstants", 56 | "type": "tuple" 57 | } 58 | ], 59 | "stateMutability": "view", 60 | "type": "function" 61 | }, 62 | { 63 | "inputs": [], 64 | "name": "snarkConstants", 65 | "outputs": [ 66 | { 67 | "components": [ 68 | { 69 | "internalType": "uint256", 70 | "name": "PLANETHASH_KEY", 71 | "type": "uint256" 72 | } 73 | ], 74 | "internalType": "struct DarkForestUtils.SnarkConstants", 75 | "name": "", 76 | "type": "tuple" 77 | } 78 | ], 79 | "stateMutability": "view", 80 | "type": "function" 81 | } 82 | ], 83 | "bytecode": "0x608060405234801561001057600080fd5b50604080518082018252338152600160208083018281527f0b8f3e5cb1e0519c520b88073ab3be1313af840e2f2682ba2e77c948b27bcb8a6000908152825292517f190130b61c65d0bc84a5bab3064c7d963327e80b54c17dbf4714f663cc5f2678805494511515600160a01b026001600160a81b03199095166001600160a01b0392909216919091179390931790925582519182019092527f04c06f1329eb7d8e37e558c329a5ffc2b896718ab7dd897b7d45feae09a285d7908190529055610160806100df6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806326c1e7501461004657806351baf19e146100f757806386b714e214610124575b600080fd5b6100bf61005436600461013b565b6040805180820190915260008082526020820152506000908152602081815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116835274010000000000000000000000000000000000000000900460ff1615159082015290565b60408051825173ffffffffffffffffffffffffffffffffffffffff168152602092830151151592810192909252015b60405180910390f35b60408051602080820183526000909152815190810190915260015481525b604051905181526020016100ee565b604080516020810190915260015481526101159081565b60006020828403121561014c578081fd5b503591905056fea164736f6c6343000803000a", 84 | "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c806326c1e7501461004657806351baf19e146100f757806386b714e214610124575b600080fd5b6100bf61005436600461013b565b6040805180820190915260008082526020820152506000908152602081815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116835274010000000000000000000000000000000000000000900460ff1615159082015290565b60408051825173ffffffffffffffffffffffffffffffffffffffff168152602092830151151592810192909252015b60405180910390f35b60408051602080820183526000909152815190810190915260015481525b604051905181526020016100ee565b604080516020810190915260015481526101159081565b60006020828403121561014c578081fd5b503591905056fea164736f6c6343000803000a", 85 | "linkReferences": {}, 86 | "deployedLinkReferences": {} 87 | } 88 | -------------------------------------------------------------------------------- /scripts/solbuilder.ts: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import { execSync } from 'child_process'; 4 | import fs from 'fs'; 5 | import path from 'path'; 6 | import { camelCase, upperCase } from './utils'; 7 | 8 | const cwd = process.cwd(); 9 | const compiledCircuitsDirectory = path.resolve(`${cwd}/compiledCircuits`); 10 | const outputDirectory = path.resolve(`${cwd}/contracts`); 11 | 12 | try { 13 | const header = 14 | `pragma solidity >=0.7.6;\n` + 15 | `pragma abicoder v2;\n` + 16 | `import "./Pairing.sol";\n`; 17 | const verify = ` 18 | using Pairing for *; 19 | struct VerifyingKey { 20 | Pairing.G1Point alfa1; 21 | Pairing.G2Point beta2; 22 | Pairing.G2Point gamma2; 23 | Pairing.G2Point delta2; 24 | Pairing.G1Point[] IC; 25 | } 26 | struct Proof { 27 | Pairing.G1Point A; 28 | Pairing.G2Point B; 29 | Pairing.G1Point C; 30 | } 31 | function verify( 32 | uint[] memory input, 33 | Proof memory proof, 34 | VerifyingKey memory vk 35 | ) internal view returns (uint) { 36 | uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 37 | require(input.length + 1 == vk.IC.length,"verifier-bad-input"); 38 | // Compute the linear combination vk_x 39 | Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); 40 | for (uint i = 0; i < input.length; i++) { 41 | require(input[i] < snark_scalar_field,"verifier-gte-snark-scalar-field"); 42 | vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i])); 43 | } 44 | vk_x = Pairing.addition(vk_x, vk.IC[0]); 45 | if (!Pairing.pairingProd4( 46 | Pairing.negate(proof.A), proof.B, 47 | vk.alfa1, vk.beta2, 48 | vk_x, vk.gamma2, 49 | proof.C, vk.delta2 50 | )) return 1; 51 | return 0; 52 | } 53 | function verifyProof( 54 | uint[2] memory a, 55 | uint[2][2] memory b, 56 | uint[2] memory c, 57 | uint[] memory input, 58 | VerifyingKey memory vk 59 | ) public view returns (bool) { 60 | Proof memory proof; 61 | proof.A = Pairing.G1Point(a[0], a[1]); 62 | proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); 63 | proof.C = Pairing.G1Point(c[0], c[1]); 64 | if (verify(input, proof, vk) == 0) { 65 | return true; 66 | } else { 67 | return false; 68 | } 69 | }`; 70 | const circuits = fs.readdirSync(`${compiledCircuitsDirectory}`); 71 | const verifiers = circuits.map(circuit => buildVerifier(circuit.split('.')[0])).join('\n'); 72 | const contract = 73 | `${header} 74 | library Verifier { 75 | ${verify} 76 | 77 | ${verifiers}}`; 78 | fs.writeFileSync(`${outputDirectory}/Verifier.sol`, contract); 79 | } catch (error) { 80 | console.log(error); 81 | process.exit(1); 82 | } 83 | 84 | function buildVerifier(circuitName) { 85 | execSync(`npx snarkjs zkey export solidityverifier ${compiledCircuitsDirectory}/${circuitName}/circuit.zkey verifier.sol`, { 86 | stdio: 'inherit', 87 | }); 88 | const data = fs.readFileSync('verifier.sol').toString(); 89 | execSync('rm ' + 'verifier.sol', { stdio: 'inherit' }); 90 | const strList = data.match('(?<=VerifyingKey memory vk.*{\n )(.*\n)*(?=\n* }\n.*function)'); 91 | const pairings = strList[0]; 92 | 93 | const numInputs = parseInt(pairings.match('(?<=vk.IC = new Pairing\.G1Point[\[\]]).*(?=;)')[0].match('\(([^()]+)\)')[0]) - 1; 94 | const body = 95 | `function ${camelCase(circuitName)}VerifyingKey() internal pure returns (VerifyingKey memory vk) { 96 | ${pairings} 97 | } 98 | function verify${upperCase(circuitName)}Proof( 99 | uint256[2] memory a, 100 | uint256[2][2] memory b, 101 | uint256[2] memory c, 102 | uint256[${numInputs}] memory input 103 | ) public view returns (bool) { 104 | uint256[] memory inputValues = new uint256[](input.length); 105 | for (uint256 i = 0; i < input.length; i++) { 106 | inputValues[i] = input[i]; 107 | } 108 | return verifyProof(a, b, c, inputValues, ${camelCase(circuitName)}VerifyingKey()); 109 | }\n`; 110 | 111 | return body; 112 | } 113 | -------------------------------------------------------------------------------- /src/utils/prover.ts: -------------------------------------------------------------------------------- 1 | import { post } from './api'; 2 | import { Snark, Stark, ZKTypes } from '../types'; 3 | import config from '../../config'; 4 | const STARKWARE_APP = config.starkwareApp; 5 | 6 | const wasmPath = '/circuits/wasm/'; 7 | const keyPath = '/circuits/keys/'; 8 | const verificationKeyPath = '/circuits/verification_keys/'; 9 | 10 | function camelCase(str) { 11 | return str.split('-').map(_ => { 12 | return _[0].toUpperCase() + _.slice(1); 13 | }).join(''); 14 | } 15 | 16 | // HELPERS 17 | async function proveSnark(circuit, inputs): Promise { 18 | // @ts-ignore 19 | const { proof, publicSignals } = await snarkjs.groth16.fullProve( 20 | inputs, 21 | wasmPath + camelCase(circuit) + '.wasm', 22 | keyPath + camelCase(circuit) + '.zkey', 23 | ); 24 | 25 | return { proof, publicSignals }; 26 | } 27 | 28 | async function proveStark(circuit, inputs): Promise { 29 | return new Promise((resolve, reject) => { 30 | post(`${STARKWARE_APP}/prove`, { circuit, inputs }).then(res => { 31 | const proof = { 32 | fact: res.res.fact, 33 | programOutputs: res.res.programOutputs.map(BigInt), 34 | }; 35 | resolve(proof); 36 | }).catch(err => { 37 | reject(err); 38 | }); 39 | }); 40 | } 41 | 42 | async function prove(circuit, inputs, zk: ZKTypes): Promise { 43 | const prover = zk === 'snark' ? proveSnark : proveStark; 44 | return prover(circuit, inputs); 45 | } 46 | 47 | async function verifyStark( 48 | circuit: string, 49 | proof: Snark & Stark, 50 | ): Promise { 51 | // send to sharp 52 | if ('programOutputs' in proof) { 53 | return new Promise(resolve => resolve(true)); 54 | } else { 55 | throw new Error('proof is not a STARK'); 56 | } 57 | } 58 | 59 | async function verifySnark( 60 | circuit: string, 61 | proof: Snark & Stark, 62 | ): Promise { 63 | if ('publicSignals' in proof) { 64 | const vKey = await fetch(verificationKeyPath + camelCase(circuit) + '.json') 65 | .then(res => { 66 | return res.json(); 67 | }); 68 | 69 | // @ts-ignore 70 | const res = await snarkjs.groth16.verify( 71 | vKey, 72 | proof.publicSignals, 73 | proof.proof, 74 | ); 75 | return res; 76 | } else { 77 | throw new Error('proof is not a SNARK'); 78 | } 79 | } 80 | 81 | async function verify( 82 | circuit: string, 83 | proof: Snark & Stark, 84 | zk: ZKTypes, 85 | ): Promise { 86 | const verifier = zk === 'snark' ? verifySnark : verifyStark; 87 | return verifier(circuit, proof); 88 | } 89 | 90 | // PROVERS 91 | export async function proveEncryption( 92 | zk: ZKTypes, 93 | args, 94 | ) { 95 | return prove('encryption', { 96 | key: args[0].toString(), 97 | seller_private_key: zk === ZKTypes.SNARK ? args[1].asCircuitInputs() : args[1].rawPrivKey.toString(), 98 | buyer_public_key: args[2].asCircuitInputs(), 99 | }, zk); 100 | } 101 | 102 | export async function proveHash(zk: ZKTypes, args) { 103 | return prove('hash', { 104 | preimage: args[1].toString(), 105 | key: args[0].toString(), 106 | salt: args[2].toString(), 107 | }, zk); 108 | } 109 | 110 | export async function proveBlur(zk: ZKTypes, args) { 111 | return prove('blur-image', { 112 | preimage: args[1].map(_ => _.toString()), 113 | key: args[0].toString(), 114 | blurred_image: args[2].map(_ => _.toString()), 115 | }, zk); 116 | } 117 | 118 | export async function proveDarkForest(zk: ZKTypes, args) { 119 | return prove('dark-forest', { 120 | x: args[1].toString(), 121 | y: args[2].toString(), 122 | key: args[0].toString(), 123 | hash: args[3].toString(), 124 | salt: args[4], 125 | }, zk); 126 | } 127 | 128 | // VERIFIERS 129 | export const verifyEncryption = async (zk: ZKTypes, proof) => verify('encryption', proof, zk); 130 | export const verifyHash = async (zk: ZKTypes, proof) => verify('hash', proof, zk); 131 | export const verifyBlur = async (zk: ZKTypes, proof) => verify('blur', proof, zk); 132 | export const verifyDarkForest = async (zk: ZKTypes, proof) => verify('dark-forest', proof, zk); 133 | -------------------------------------------------------------------------------- /src/pages/content/Blur.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import styled from 'styled-components'; 3 | import assert from 'assert'; 4 | 5 | import { 6 | blurImage, 7 | ZKFunctions, 8 | } from '../../utils/crypto'; 9 | 10 | import { 11 | InputProps, 12 | } from '../../types/content'; 13 | import { 14 | ZKTypes, 15 | } from '../../types'; 16 | 17 | 18 | type PixelProps = { 19 | bit: number; 20 | } 21 | 22 | const Pixel = styled.div` 23 | display: flex; 24 | flex: 1; 25 | height: 100%; 26 | background-color: ${props => (props.bit === 0 ? 27 | 'black' : 'white')}; 28 | `; 29 | 30 | type ImageWrapperProps = { 31 | height: string; 32 | } 33 | 34 | const ImageWrapper = styled.div` 35 | justify-content: space-between; 36 | border: 1px solid black; 37 | display: flex; 38 | flex-direction: row; 39 | height: ${props => props.height}; 40 | width: 100%; 41 | align-items: center; 42 | box-sizing: border-box; 43 | `; 44 | 45 | type ImageProps = { 46 | property: number[]; 47 | height: string; 48 | message?: number[]; 49 | } 50 | 51 | const Image = (props: ImageProps) => { 52 | const bits = props.message ? props.message : props.property; 53 | 54 | return ( 55 | 56 | {bits.map((bit, index) => ( 57 | 58 | ))} 59 | 60 | ); 61 | }; 62 | 63 | type ImageLargeProps = { 64 | property: number[]; 65 | message?: number[]; 66 | } 67 | 68 | const ImageLarge = (props: ImageLargeProps) => { 69 | return ( 70 | 71 | ); 72 | }; 73 | 74 | const ImageSmall = (props: ImageLargeProps) => { 75 | return ( 76 | 77 | ); 78 | }; 79 | const ImageInput = (props: InputProps) => { 80 | 81 | useEffect(() => { 82 | props.setPreimage('0000000000000000'); 83 | }, []); 84 | 85 | const onChange = (index: number) => () => { 86 | let preimageArray = props.preimage.split('').map(Number); 87 | preimageArray[index] = (preimageArray[index] + 1) % 2; 88 | const newPreimage = preimageArray.join(''); 89 | props.setPreimage(newPreimage); 90 | }; 91 | 92 | return ( 93 | <> 94 | 95 | {props.preimage.split('').map((bit, index) => { 96 | return ( 97 | 102 | ); 103 | })} 104 | 105 | 106 | 107 | ); 108 | }; 109 | 110 | const isNumberArray = (value) => { 111 | return Array.isArray(value) && 112 | typeof value[0] === 'number'; 113 | }; 114 | 115 | const assertContent = (content) => { 116 | assert(isNumberArray(content.cipher)); 117 | assert(isNumberArray(content.property)); 118 | }; 119 | 120 | const assertMessage = isNumberArray; 121 | 122 | const computeProperty = (preimage, key) => { 123 | const preimageArray = preimage.split('').map(Number); 124 | const blurredImage = blurImage(preimageArray, key); 125 | return [preimageArray, blurredImage]; 126 | }; 127 | 128 | const assertProofInputs = (args: any[]) => { 129 | assert(args.length === 3); 130 | assert(typeof args[0] === 'bigint'); 131 | assert(isNumberArray(args[1])); 132 | assert(isNumberArray(args[2])); 133 | }; 134 | 135 | const json = { 136 | input: ImageInput, 137 | display: ImageLarge, 138 | list: ImageSmall, 139 | decrypt: (zk, a, b) => blurImage(a, b), 140 | computeProperty, 141 | prover: { 142 | [ZKTypes.SNARK]: ZKFunctions[ZKTypes.SNARK].provers.blur, 143 | [ZKTypes.STARK]: ZKFunctions[ZKTypes.STARK].provers.blur, 144 | }, 145 | verifier: { 146 | [ZKTypes.SNARK]: ZKFunctions[ZKTypes.SNARK].verifiers.blur, 147 | [ZKTypes.STARK]: ZKFunctions[ZKTypes.STARK].verifiers.blur, 148 | }, 149 | assertProofInputs, 150 | assertContent, 151 | assertMessage, 152 | }; 153 | export default json; 154 | -------------------------------------------------------------------------------- /public/circuits/verification_keys/Hash.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth16", 3 | "curve": "bn128", 4 | "nPublic": 5, 5 | "vk_alpha_1": [ 6 | "19642524115522290447760970021746675789341356000653265441069630957431566301675", 7 | "15809037446102219312954435152879098683824559980020626143453387822004586242317", 8 | "1" 9 | ], 10 | "vk_beta_2": [ 11 | [ 12 | "3306678135584565297353192801602995509515651571902196852074598261262327790404", 13 | "6402738102853475583969787773506197858266321704623454181848954418090577674938" 14 | ], 15 | [ 16 | "4983765881427969364617654516554524254158908221590807345159959200407712579883", 17 | "15158588411628049902562758796812667714664232742372443470614751812018801551665" 18 | ], 19 | [ 20 | "1", 21 | "0" 22 | ] 23 | ], 24 | "vk_gamma_2": [ 25 | [ 26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781", 27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634" 28 | ], 29 | [ 30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930", 31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531" 32 | ], 33 | [ 34 | "1", 35 | "0" 36 | ] 37 | ], 38 | "vk_delta_2": [ 39 | [ 40 | "2528491300866434509699704412642731178102268865012248785813458721505586631446", 41 | "11078114351411396302492606863995638386506537365844689646898417550998267219414" 42 | ], 43 | [ 44 | "17448560587075395769884970409122010185777125947946128673908172602768905142360", 45 | "7646900014588577959937375249841784560277351960820231527167492175864420231155" 46 | ], 47 | [ 48 | "1", 49 | "0" 50 | ] 51 | ], 52 | "vk_alphabeta_12": [ 53 | [ 54 | [ 55 | "5990981426838611487817331801723298853689232371402636505882752025826717861263", 56 | "6471600557194756298005957410208198947034348214534657394165503369790309076215" 57 | ], 58 | [ 59 | "15002700749801870140343913307313851505416165248926784119266834715141460279528", 60 | "3830910611342760800353177932876565223705290639657811356514431363408808083245" 61 | ], 62 | [ 63 | "16213272029928492311641815345584648151738099578429924053941653329679116056011", 64 | "11521059852145391195901894023171250860281638548552692281491114389569775909273" 65 | ] 66 | ], 67 | [ 68 | [ 69 | "21473208233673181545713372461262158409524966660539444635240255266528451743613", 70 | "12831374732506919217314780439582796881729424911714976211545279975915712437524" 71 | ], 72 | [ 73 | "6384272187442204077342066657702496903903539015888454871400719146512483196930", 74 | "11896033867460826226898768849934024947887432723445089197160889467704536005586" 75 | ], 76 | [ 77 | "4320872437228236846208766625647342285071164667482133950438695528545836489222", 78 | "13210525034366360416090582915961576197528924758377030567364985602065069567971" 79 | ] 80 | ] 81 | ], 82 | "IC": [ 83 | [ 84 | "6649706228940092214024537715615750974782507177962522143585713102098252140507", 85 | "8600012682628506602820352378092933216847589691662499915982120618606494269693", 86 | "1" 87 | ], 88 | [ 89 | "11890359990516516306620937012547994802495225911011046267065388026614930788971", 90 | "7640518447137566552605716762042533071088153492924720917575059134719958960794", 91 | "1" 92 | ], 93 | [ 94 | "13240033539492298308581903484700265342341622504201213204034038067339035592231", 95 | "2085478624939869822204404203546372376628476589570140288168203331015267633255", 96 | "1" 97 | ], 98 | [ 99 | "17074828529152127823096532362911641732470042415519965643143486759850463393976", 100 | "19176684664061417791363598996003389146087993907369817080564093102545749703925", 101 | "1" 102 | ], 103 | [ 104 | "3487870124006761499757286140356040193039885261060458946754054319861912246688", 105 | "14227488130452521732880752674651927713352840101996664468289157682022926222454", 106 | "1" 107 | ], 108 | [ 109 | "17243644912270822364454898995391259659202910174472139797545967979755313574588", 110 | "18321263016295783859814954531795102538581144887377749037991635164191301750260", 111 | "1" 112 | ] 113 | ] 114 | } -------------------------------------------------------------------------------- /public/circuits/verification_keys/Encryption.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth16", 3 | "curve": "bn128", 4 | "nPublic": 5, 5 | "vk_alpha_1": [ 6 | "19642524115522290447760970021746675789341356000653265441069630957431566301675", 7 | "15809037446102219312954435152879098683824559980020626143453387822004586242317", 8 | "1" 9 | ], 10 | "vk_beta_2": [ 11 | [ 12 | "3306678135584565297353192801602995509515651571902196852074598261262327790404", 13 | "6402738102853475583969787773506197858266321704623454181848954418090577674938" 14 | ], 15 | [ 16 | "4983765881427969364617654516554524254158908221590807345159959200407712579883", 17 | "15158588411628049902562758796812667714664232742372443470614751812018801551665" 18 | ], 19 | [ 20 | "1", 21 | "0" 22 | ] 23 | ], 24 | "vk_gamma_2": [ 25 | [ 26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781", 27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634" 28 | ], 29 | [ 30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930", 31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531" 32 | ], 33 | [ 34 | "1", 35 | "0" 36 | ] 37 | ], 38 | "vk_delta_2": [ 39 | [ 40 | "2528491300866434509699704412642731178102268865012248785813458721505586631446", 41 | "11078114351411396302492606863995638386506537365844689646898417550998267219414" 42 | ], 43 | [ 44 | "17448560587075395769884970409122010185777125947946128673908172602768905142360", 45 | "7646900014588577959937375249841784560277351960820231527167492175864420231155" 46 | ], 47 | [ 48 | "1", 49 | "0" 50 | ] 51 | ], 52 | "vk_alphabeta_12": [ 53 | [ 54 | [ 55 | "5990981426838611487817331801723298853689232371402636505882752025826717861263", 56 | "6471600557194756298005957410208198947034348214534657394165503369790309076215" 57 | ], 58 | [ 59 | "15002700749801870140343913307313851505416165248926784119266834715141460279528", 60 | "3830910611342760800353177932876565223705290639657811356514431363408808083245" 61 | ], 62 | [ 63 | "16213272029928492311641815345584648151738099578429924053941653329679116056011", 64 | "11521059852145391195901894023171250860281638548552692281491114389569775909273" 65 | ] 66 | ], 67 | [ 68 | [ 69 | "21473208233673181545713372461262158409524966660539444635240255266528451743613", 70 | "12831374732506919217314780439582796881729424911714976211545279975915712437524" 71 | ], 72 | [ 73 | "6384272187442204077342066657702496903903539015888454871400719146512483196930", 74 | "11896033867460826226898768849934024947887432723445089197160889467704536005586" 75 | ], 76 | [ 77 | "4320872437228236846208766625647342285071164667482133950438695528545836489222", 78 | "13210525034366360416090582915961576197528924758377030567364985602065069567971" 79 | ] 80 | ] 81 | ], 82 | "IC": [ 83 | [ 84 | "6696830915188811222805337860434242118015796195504552362503772065564241004793", 85 | "14168315932310402745458233483865755546062824270218045859290041083862651822454", 86 | "1" 87 | ], 88 | [ 89 | "21797365513790993772188867392601402947083186120404804982493953694003686130098", 90 | "10894181459181605005323314653042322574935689496365271983143352198223111959228", 91 | "1" 92 | ], 93 | [ 94 | "13956123995938546240142728085826425531645707098551729463962137646146316756078", 95 | "1015579278733704732152149441790371937618646964903561859133106159079756082705", 96 | "1" 97 | ], 98 | [ 99 | "1406513136486938335399692418160257047861625989046073447629101086191159002553", 100 | "5501588824195265410437141777891417923446988980418639101973843701710315702890", 101 | "1" 102 | ], 103 | [ 104 | "13116752893921034560348126282679915303501261626202282210808233325523938302596", 105 | "8014465560771828168190678410134247897388632687630536200036146352367517129204", 106 | "1" 107 | ], 108 | [ 109 | "6268772482625100927397258823484197390170769634851849637235756810655726976365", 110 | "18548977206527417647915185397219583535313858552278826900129808792864343191398", 111 | "1" 112 | ] 113 | ] 114 | } -------------------------------------------------------------------------------- /scripts/builder.ts: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import { execSync } from 'child_process'; 4 | import fs from 'fs'; 5 | import path from 'path'; 6 | import { upperCase } from './utils'; 7 | 8 | const wasmOutPath = 'wasm'; 9 | const zkeyOutPath = 'keys'; 10 | const verificationOutPath = 'verification_keys'; 11 | const deterministic = process.argv[2] === 'true'; 12 | const circuitsList = process.argv[3]; 13 | let potFile = process.argv[4]; 14 | 15 | // TODO: add an option to generate with entropy for production keys 16 | 17 | if (process.argv.length !== 5) { 18 | console.log('usage'); 19 | console.log( 20 | 'builder comma,seperated,list,of,circuits wasm_out_path zkey_out_path [`true` if deterministic / `false` if not] pot_size \n for example, $ node circuits/builder.js . . false sig-check 20' 21 | ); 22 | process.exit(1); 23 | } 24 | 25 | if (!potFile) { 26 | potFile = '15'; 27 | } 28 | 29 | const cwd = process.cwd(); 30 | const circuitsDirectory = path.resolve(`${cwd}/circuits`); 31 | const outputDirectory = path.resolve(`${cwd}/compiledCircuits`); 32 | 33 | if (!fs.existsSync(`${outputDirectory}`)) { 34 | fs.mkdirSync(`${outputDirectory}`); 35 | } 36 | 37 | for (let circuitName of circuitsList.split(',')) { 38 | if (deterministic && !process.env['beacon']) { 39 | console.log("ERROR! Can't find a sourced .env with a beacon variable"); 40 | process.exit(1); 41 | } 42 | 43 | if (!fs.existsSync(`${outputDirectory}/${circuitName}`)) { 44 | fs.mkdirSync(`${outputDirectory}/${circuitName}`); 45 | } 46 | process.chdir(`${circuitsDirectory}/${circuitName}`); 47 | 48 | try { 49 | execSync('npx circom circuit.circom --r1cs --wasm --sym', { 50 | stdio: 'inherit', 51 | }); 52 | execSync('npx snarkjs r1cs info circuit.r1cs', { stdio: 'inherit' }); 53 | execSync( 54 | "npx snarkjs zkey new circuit.r1cs '" + 55 | circuitsDirectory + 56 | '/pots/pot' + 57 | potFile + 58 | "_final.ptau' circuit_" + 59 | circuitName + 60 | '.zkey', 61 | { stdio: 'inherit' } 62 | ); 63 | if (deterministic) { 64 | execSync( 65 | 'npx snarkjs zkey beacon circuit_' + 66 | circuitName + 67 | '.zkey circuit.zkey ' + 68 | process.env['beacon'] + 69 | ' 10', 70 | { stdio: 'inherit' } 71 | ); 72 | } else { 73 | execSync( 74 | 'npx snarkjs zkey contribute circuit_' + 75 | circuitName + 76 | '.zkey circuit.zkey ' + 77 | `-e='${Date.now()}'`, 78 | { stdio: 'inherit' } 79 | ); 80 | } 81 | execSync( 82 | 'npx snarkjs zkey export verificationkey circuit.zkey verification_key.json', 83 | { stdio: 'inherit' } 84 | ); 85 | execSync( 86 | 'npx snarkjs wtns calculate circuit.wasm input.json witness.wtns', 87 | { 88 | stdio: 'inherit', 89 | } 90 | ); 91 | execSync( 92 | 'npx snarkjs groth16 prove circuit.zkey witness.wtns proof.json public.json', 93 | { stdio: 'inherit' } 94 | ); 95 | execSync( 96 | 'npx snarkjs groth16 verify verification_key.json public.json proof.json', 97 | { stdio: 'inherit' } 98 | ); 99 | fs.copyFileSync( 100 | 'circuit.wasm', 101 | `${outputDirectory}/${circuitName}/circuit.wasm` 102 | ); 103 | fs.copyFileSync( 104 | 'circuit.zkey', 105 | `${outputDirectory}/${circuitName}/circuit.zkey` 106 | ); 107 | fs.copyFileSync( 108 | 'verification_key.json', 109 | `${outputDirectory}/${circuitName}/verification_key.json` 110 | ); 111 | execSync('rm circuit.wasm', { stdio: 'inherit' }); 112 | execSync('rm circuit.zkey', { stdio: 'inherit' }); 113 | execSync(`rm circuit_${circuitName}.zkey`, { stdio: 'inherit' }); 114 | execSync('rm verification_key.json', { stdio: 'inherit' }); 115 | execSync('rm witness.wtns', { stdio: 'inherit' }); 116 | execSync('rm circuit.r1cs', { stdio: 'inherit' }); 117 | execSync('rm circuit.sym', { stdio: 'inherit' }); 118 | execSync('rm proof.json', { stdio: 'inherit' }); 119 | } catch (error) { 120 | console.log(error); 121 | process.exit(1); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EthDataMarketplace 2 | 3 | ## How it works 4 | 5 | 1. Creator generates a [Property Proof](#property-proofs) which: 6 | - asserts they have some content which satisfies a particular property (`f(data) = property`) 7 | - outputs the encryption of the data with a randomly generated key (`Enc(data, key)`) 8 | - outputs the hash of the randomly generated key (`H(key)`) 9 | 2. The creator publishes the proof on IPFS and registers the IPFS URI and the key hash on a smart contract 10 | 3. Using the IPFS URI on the contract, the buyer sees the property and verifies that: 11 | - the [Property Proof](#property-proofs) is valid 12 | - the output key hash in the proof matches the key hash stored on the contract (if this is unchecked the creator could maliciously submit two different keys to the smart contract and the [Property Proof](#property-proofs), so the buyer won't be able to successfully retrieve the content) 13 | 4. if valid, the buyer will purchase the token and lock ETH in the contract 14 | 5. the creator will generate an [Encryption Proof](#encryption-proof) which: 15 | - asserts that they have the preimage to the key hash stored on the contract (`H(preimage) = H(key)`) 16 | - outputs the encryption of the preimage with the buyer's public key (`Enc(preimage, pk)`) 17 | if the proof is valid, the ETH is redeemed from the contract 18 | 19 | 6. the buyer retrieves the encrypted key (`Enc(key, pk)`) from the contract computes 20 | - `Dec(Enc(key, pk), sk) => key` 21 | - `Dec(Enc(data, key), key) => data` 22 | 23 | ## Circuits 24 | 25 | The circuits are divided into two types [Encryption Proofs](#encryption-proof) and [Property Proofs](#property-proofs). The *Encryption Proof* is the proof sent over the contract by the seller to verify the decryption key exchange. The *Property Proofs* are the proofs published on IPFS by the seller to commit to the property of the content. 26 | 27 | ### Encryption Proof 28 | 29 | The proof sent by the seller in step 5, which 30 | - asserts that they have the preimage to the key hash stored on the contract (`H(preimage) = H(key)`) 31 | - outputs the encryption of the preimage with the buyer's public key (`Enc(preimage, pk)`) 32 | 33 | #### encryption 34 | 35 | ##### Inputs 36 | | signal | private | type | description | 37 | |-|-|-|-| 38 | | key | true | BigInt | | 39 | | private_key | true | BigInt | | 40 | | hash | false | BigInt | | 41 | | public_key | false | Array[2] | | 42 | 43 | ##### Outputs 44 | | signal | type | description | 45 | |-|-|-| 46 | | out | Array[2] | | 47 | 48 | ### Property Proofs 49 | 50 | The proof published by the seller on IPFS in step 1-2, which 51 | - asserts they have some content which satisfies a particular property (`f(data) = property`) 52 | - outputs the encryption of the data with a randomly generated key (`Enc(key, pk)`) 53 | - outputs the hash of the randomly generated key (`H(key)`) 54 | 55 | Below are possible properties that can be committed and verified. 56 | 57 | #### hash 58 | 59 | Commits the MiMC hash of the content. 60 | ```f(data) = H(data)``` 61 | 62 | ##### Inputs 63 | | signal | private | type | description | 64 | |-|-|-|-| 65 | | preimage | true | BigInt | | 66 | | key | true | BigInt | | 67 | | hash | false | BigInt | | 68 | | salt | false | BigInt | | 69 | 70 | ##### Outputs 71 | | signal | type | description | 72 | |-|-|-| 73 | | key_hash | BigInt | | 74 | | ciphertext | Array[2] | | 75 | 76 | #### dark-forest 77 | 78 | Commits the MiMC hash of the `x` and `y` coordinates of a Dark Forest planet. 79 | 80 | ##### Inputs 81 | | signal | private | type | description | 82 | |-|-|-|-| 83 | | x | true | BigInt | | 84 | | y | true | BigInt | | 85 | | key | true | BigInt | | 86 | | hash | false | BigInt | | 87 | | salt | false | BigInt | | 88 | 89 | ##### Outputs 90 | | signal | type | description | 91 | |-|-|-| 92 | | key_hash | BigInt | | 93 | | ciphertext | Array[3] | | 94 | 95 | #### blur-image 96 | 97 | Commits the XOR of a bitmap with a `key`. 98 | 99 | > Note: Obviously, this means the property is nothing more than a random bitmap that is non-unique to the content and doesn't give any indication to the buyer of what they are purchasing. This was made as a quick example of another property and an early attempt at proving a property of an image. Hopefully, this can be improved in the future to be more applicable. 100 | 101 | ##### Inputs 102 | | signal | private | type | description | 103 | |-|-|-|-| 104 | | preimage | true | Array[N] | | 105 | | key | true | BigInt | | 106 | | blurred_image | false | Array[N] | | 107 | 108 | ##### Outputs 109 | | signal | type | description | 110 | |-|-|-| 111 | | hash | BigInt | | 112 | | computed_image | Array[N] | | 113 | -------------------------------------------------------------------------------- /src/app/Ipfs.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import Modal from '../components/Modal'; 5 | import TextInput from '../components/TextInput'; 6 | import Toggle from '../components/Toggle'; 7 | import { Header } from '../components/text'; 8 | import ipfs from '../utils/ipfs'; 9 | import config from '../../config'; 10 | 11 | 12 | type IconProps = { 13 | connected: boolean; 14 | } 15 | 16 | const ConnectedIcon = styled.div` 17 | border-radius: ${props => props.theme.borderRadii.circle}; 18 | width: 10px; 19 | height: 10px; 20 | background-color: ${props => props.connected ? 'green' : 'yellow'}; 21 | border: 1px solid black; 22 | margin-right: auto; 23 | `; 24 | 25 | const IpfsLabel = styled.div` 26 | margin-right: auto; 27 | `; 28 | 29 | const IpfsIcon = styled.div` 30 | display: flex; 31 | border: ${props => `1px solid ${props.theme.color.grey30}`}; 32 | height: ${props => props.theme.spacing(5)}; 33 | padding: 0px 10px; 34 | align-items: center; 35 | border-radius: ${props => props.theme.borderRadii.curvy}; 36 | :hover { 37 | cursor: pointer; 38 | background-color: ${props => props.theme.color.grey10}; 39 | } 40 | flex: 1; 41 | margin-right: 10px; 42 | box-sizing: border-box; 43 | `; 44 | 45 | const ModalHeader = styled(Header)` 46 | margin-top: auto; 47 | margin-bottom: auto; 48 | `; 49 | 50 | enum Providers { 51 | HOST = 'host', 52 | INFURA = 'infura', 53 | LOCAL = 'local', 54 | } 55 | 56 | export default function Ipfs() { 57 | const [modalShow, setModalShow] = useState(false); 58 | const [provider, setProvider] = useState(Providers.INFURA); 59 | const [host, setHost] = useState(ipfs.host); 60 | const [protocol, setProtocol] = useState(ipfs.protocol); 61 | const [infuraProjectId, setInfuraProjectId] = 62 | useState(ipfs.infuraProjectId); 63 | const [infuraProjectSecret, setInfuraProjectSecret] = 64 | useState(ipfs.infuraProjectSecret); 65 | const [connected, setConnected] = useState(false); 66 | const [didMount, setDidMount] = useState(false); 67 | 68 | useEffect(() => { 69 | switch (ipfs.host) { 70 | case 'host': 71 | setProvider(Providers.HOST); 72 | break; 73 | case 'infura': 74 | setProvider(Providers.INFURA); 75 | break; 76 | case 'localhost': 77 | setProvider(Providers.LOCAL); 78 | break; 79 | } 80 | }, []); 81 | 82 | useEffect(() => { 83 | ipfs.checkConnection().then(setConnected); 84 | setDidMount(true); 85 | return () => setDidMount(false); 86 | }, [provider]); 87 | 88 | const onShow = () => { 89 | setModalShow(true); 90 | }; 91 | 92 | const onClose = () => { 93 | onEnter(); 94 | setModalShow(false); 95 | }; 96 | 97 | const onEnter = () => { 98 | ipfs.updateSettings(host, protocol, infuraProjectId, infuraProjectSecret); 99 | }; 100 | 101 | const onSetProvider = (_provider: Providers) => { 102 | setProvider(_provider); 103 | switch(_provider) { 104 | case Providers.LOCAL: 105 | setHost('localhost'); 106 | setProtocol('http'); 107 | break; 108 | case Providers.INFURA: 109 | setHost('ipfs.infura.io'); 110 | setProtocol('https'); 111 | break; 112 | case Providers.HOST: 113 | setHost(config.ipfsHost); 114 | setProtocol('http'); 115 | break; 116 | } 117 | }; 118 | 119 | return ( 120 | <> 121 | 122 | 123 | 124 | IPFS 125 | 126 | 127 | 128 | Change IPFS Settings 129 | 134 | {provider === Providers.HOST ? 135 | null : 136 | <> 137 | 143 | 149 |
150 | {provider === Providers.INFURA ? 151 | <> 152 | 158 | 164 | 165 | : null} 166 | 167 | } 168 |
169 | 170 | ); 171 | } 172 | --------------------------------------------------------------------------------