├── .gitignore ├── .travis.yml ├── Pipfile ├── Pipfile.lock ├── README.md ├── requirements.txt └── src ├── __init__.py ├── innerproduct ├── __init__.py ├── inner_product_prover.py └── inner_product_verifier.py ├── main.py ├── pippenger ├── __init__.py ├── group.py ├── modp.py └── pippenger.py ├── rangeproofs ├── __init__.py ├── rangeproof_aggreg_prover.py ├── rangeproof_aggreg_verifier.py ├── rangeproof_prover.py └── rangeproof_verifier.py ├── tests ├── __init__.py ├── test_aggreg_rangeproofs.py ├── test_innerprod.py ├── test_rangeproofs.py └── test_utils.py └── utils ├── __init__.py ├── commitments.py ├── elliptic_curve_hash.py ├── transcript.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | fastecdsa/ 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Environments 88 | .env 89 | .venv 90 | env/ 91 | venv/ 92 | ENV/ 93 | env.bak/ 94 | venv.bak/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | matrix: 3 | include: 4 | - python: 3.6 5 | dist: trusty 6 | sudo: false 7 | - python: 3.7 8 | dist: xenial 9 | sudo: true 10 | before_install: 11 | - sudo apt-get install python-dev libgmp3-dev 12 | - wget https://github.com/AntonKueltz/fastecdsa/archive/master.zip 13 | - unzip master.zip 14 | - cd fastecdsa-master && python setup.py install 15 | - cd .. 16 | 17 | install: 18 | - pip install -r requirements.txt 19 | 20 | script: coverage run --source=src -m unittest 21 | 22 | after_success: 23 | - codecov -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | flake8 = "*" 8 | mypy = "*" 9 | 10 | [packages] 11 | appdirs = "==1.4.3" 12 | astroid = "==2.2.5" 13 | attrs = "==19.1.0" 14 | black = "==19.3b0" 15 | certifi = "==2019.6.16" 16 | chardet = "==3.0.4" 17 | click = "==7.0" 18 | codecov = "==2.0.15" 19 | coverage = "==4.5.3" 20 | ecdsa = "==0.13.2" 21 | idna = "==2.8" 22 | isort = "==4.3.21" 23 | lazy-object-proxy = "==1.4.1" 24 | mccabe = "==0.6.1" 25 | mpmath = "==1.1.0" 26 | mypy = "==0.720" 27 | pycryptodome = "==3.8.2" 28 | pylint = "==2.3.1" 29 | requests = "==2.22.0" 30 | six = "==1.12.0" 31 | sympy = "==1.4" 32 | toml = "==0.10.0" 33 | typed-ast = "==1.4.0" 34 | typing-extensions = "==3.7.4" 35 | urllib3 = "==1.25.3" 36 | wrapt = "==1.11.2" 37 | yapf = "==0.28.0" 38 | mypy_extensions = "==0.4.1" 39 | 40 | [requires] 41 | python_version = "3.7" 42 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "c829296e1c8a4904c832c0513ce73f3b1a6a4abf85c132256e30f4521ee0a0c0" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "appdirs": { 20 | "hashes": [ 21 | "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", 22 | "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e" 23 | ], 24 | "index": "pypi", 25 | "version": "==1.4.3" 26 | }, 27 | "astroid": { 28 | "hashes": [ 29 | "sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4", 30 | "sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4" 31 | ], 32 | "index": "pypi", 33 | "version": "==2.2.5" 34 | }, 35 | "attrs": { 36 | "hashes": [ 37 | "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", 38 | "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" 39 | ], 40 | "index": "pypi", 41 | "version": "==19.1.0" 42 | }, 43 | "black": { 44 | "hashes": [ 45 | "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", 46 | "sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c" 47 | ], 48 | "index": "pypi", 49 | "version": "==19.3b0" 50 | }, 51 | "certifi": { 52 | "hashes": [ 53 | "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", 54 | "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" 55 | ], 56 | "index": "pypi", 57 | "version": "==2019.6.16" 58 | }, 59 | "chardet": { 60 | "hashes": [ 61 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 62 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 63 | ], 64 | "index": "pypi", 65 | "version": "==3.0.4" 66 | }, 67 | "click": { 68 | "hashes": [ 69 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 70 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 71 | ], 72 | "index": "pypi", 73 | "version": "==7.0" 74 | }, 75 | "codecov": { 76 | "hashes": [ 77 | "sha256:8ed8b7c6791010d359baed66f84f061bba5bd41174bf324c31311e8737602788", 78 | "sha256:ae00d68e18d8a20e9c3288ba3875ae03db3a8e892115bf9b83ef20507732bed4" 79 | ], 80 | "index": "pypi", 81 | "version": "==2.0.15" 82 | }, 83 | "coverage": { 84 | "hashes": [ 85 | "sha256:3684fabf6b87a369017756b551cef29e505cb155ddb892a7a29277b978da88b9", 86 | "sha256:39e088da9b284f1bd17c750ac672103779f7954ce6125fd4382134ac8d152d74", 87 | "sha256:3c205bc11cc4fcc57b761c2da73b9b72a59f8d5ca89979afb0c1c6f9e53c7390", 88 | "sha256:465ce53a8c0f3a7950dfb836438442f833cf6663d407f37d8c52fe7b6e56d7e8", 89 | "sha256:48020e343fc40f72a442c8a1334284620f81295256a6b6ca6d8aa1350c763bbe", 90 | "sha256:5296fc86ab612ec12394565c500b412a43b328b3907c0d14358950d06fd83baf", 91 | "sha256:5f61bed2f7d9b6a9ab935150a6b23d7f84b8055524e7be7715b6513f3328138e", 92 | "sha256:68a43a9f9f83693ce0414d17e019daee7ab3f7113a70c79a3dd4c2f704e4d741", 93 | "sha256:6b8033d47fe22506856fe450470ccb1d8ba1ffb8463494a15cfc96392a288c09", 94 | "sha256:7ad7536066b28863e5835e8cfeaa794b7fe352d99a8cded9f43d1161be8e9fbd", 95 | "sha256:7bacb89ccf4bedb30b277e96e4cc68cd1369ca6841bde7b005191b54d3dd1034", 96 | "sha256:839dc7c36501254e14331bcb98b27002aa415e4af7ea039d9009409b9d2d5420", 97 | "sha256:8f9a95b66969cdea53ec992ecea5406c5bd99c9221f539bca1e8406b200ae98c", 98 | "sha256:932c03d2d565f75961ba1d3cec41ddde00e162c5b46d03f7423edcb807734eab", 99 | "sha256:988529edadc49039d205e0aa6ce049c5ccda4acb2d6c3c5c550c17e8c02c05ba", 100 | "sha256:998d7e73548fe395eeb294495a04d38942edb66d1fa61eb70418871bc621227e", 101 | "sha256:9de60893fb447d1e797f6bf08fdf0dbcda0c1e34c1b06c92bd3a363c0ea8c609", 102 | "sha256:9e80d45d0c7fcee54e22771db7f1b0b126fb4a6c0a2e5afa72f66827207ff2f2", 103 | "sha256:a545a3dfe5082dc8e8c3eb7f8a2cf4f2870902ff1860bd99b6198cfd1f9d1f49", 104 | "sha256:a5d8f29e5ec661143621a8f4de51adfb300d7a476224156a39a392254f70687b", 105 | "sha256:aca06bfba4759bbdb09bf52ebb15ae20268ee1f6747417837926fae990ebc41d", 106 | "sha256:bb23b7a6fd666e551a3094ab896a57809e010059540ad20acbeec03a154224ce", 107 | "sha256:bfd1d0ae7e292105f29d7deaa9d8f2916ed8553ab9d5f39ec65bcf5deadff3f9", 108 | "sha256:c62ca0a38958f541a73cf86acdab020c2091631c137bd359c4f5bddde7b75fd4", 109 | "sha256:c709d8bda72cf4cd348ccec2a4881f2c5848fd72903c185f363d361b2737f773", 110 | "sha256:c968a6aa7e0b56ecbd28531ddf439c2ec103610d3e2bf3b75b813304f8cb7723", 111 | "sha256:df785d8cb80539d0b55fd47183264b7002077859028dfe3070cf6359bf8b2d9c", 112 | "sha256:f406628ca51e0ae90ae76ea8398677a921b36f0bd71aab2099dfed08abd0322f", 113 | "sha256:f46087bbd95ebae244a0eda01a618aff11ec7a069b15a3ef8f6b520db523dcf1", 114 | "sha256:f8019c5279eb32360ca03e9fac40a12667715546eed5c5eb59eb381f2f501260", 115 | "sha256:fc5f4d209733750afd2714e9109816a29500718b32dd9a5db01c0cb3a019b96a" 116 | ], 117 | "index": "pypi", 118 | "version": "==4.5.3" 119 | }, 120 | "ecdsa": { 121 | "hashes": [ 122 | "sha256:20c17e527e75acad8f402290e158a6ac178b91b881f941fc6ea305bfdfb9657c", 123 | "sha256:5c034ffa23413ac923541ceb3ac14ec15a0d2530690413bff58c12b80e56d884" 124 | ], 125 | "index": "pypi", 126 | "version": "==0.13.2" 127 | }, 128 | "idna": { 129 | "hashes": [ 130 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 131 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 132 | ], 133 | "index": "pypi", 134 | "version": "==2.8" 135 | }, 136 | "isort": { 137 | "hashes": [ 138 | "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", 139 | "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd" 140 | ], 141 | "index": "pypi", 142 | "version": "==4.3.21" 143 | }, 144 | "lazy-object-proxy": { 145 | "hashes": [ 146 | "sha256:159a745e61422217881c4de71f9eafd9d703b93af95618635849fe469a283661", 147 | "sha256:23f63c0821cc96a23332e45dfaa83266feff8adc72b9bcaef86c202af765244f", 148 | "sha256:3b11be575475db2e8a6e11215f5aa95b9ec14de658628776e10d96fa0b4dac13", 149 | "sha256:3f447aff8bc61ca8b42b73304f6a44fa0d915487de144652816f950a3f1ab821", 150 | "sha256:4ba73f6089cd9b9478bc0a4fa807b47dbdb8fad1d8f31a0f0a5dbf26a4527a71", 151 | "sha256:4f53eadd9932055eac465bd3ca1bd610e4d7141e1278012bd1f28646aebc1d0e", 152 | "sha256:64483bd7154580158ea90de5b8e5e6fc29a16a9b4db24f10193f0c1ae3f9d1ea", 153 | "sha256:6f72d42b0d04bfee2397aa1862262654b56922c20a9bb66bb76b6f0e5e4f9229", 154 | "sha256:7c7f1ec07b227bdc561299fa2328e85000f90179a2f44ea30579d38e037cb3d4", 155 | "sha256:7c8b1ba1e15c10b13cad4171cfa77f5bb5ec2580abc5a353907780805ebe158e", 156 | "sha256:8559b94b823f85342e10d3d9ca4ba5478168e1ac5658a8a2f18c991ba9c52c20", 157 | "sha256:a262c7dfb046f00e12a2bdd1bafaed2408114a89ac414b0af8755c696eb3fc16", 158 | "sha256:acce4e3267610c4fdb6632b3886fe3f2f7dd641158a843cf6b6a68e4ce81477b", 159 | "sha256:be089bb6b83fac7f29d357b2dc4cf2b8eb8d98fe9d9ff89f9ea6012970a853c7", 160 | "sha256:bfab710d859c779f273cc48fb86af38d6e9210f38287df0069a63e40b45a2f5c", 161 | "sha256:c10d29019927301d524a22ced72706380de7cfc50f767217485a912b4c8bd82a", 162 | "sha256:dd6e2b598849b3d7aee2295ac765a578879830fb8966f70be8cd472e6069932e", 163 | "sha256:e408f1eacc0a68fed0c08da45f31d0ebb38079f043328dce69ff133b95c29dc1" 164 | ], 165 | "index": "pypi", 166 | "version": "==1.4.1" 167 | }, 168 | "mccabe": { 169 | "hashes": [ 170 | "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", 171 | "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" 172 | ], 173 | "index": "pypi", 174 | "version": "==0.6.1" 175 | }, 176 | "mpmath": { 177 | "hashes": [ 178 | "sha256:fc17abe05fbab3382b61a123c398508183406fa132e0223874578e20946499f6" 179 | ], 180 | "index": "pypi", 181 | "version": "==1.1.0" 182 | }, 183 | "mypy": { 184 | "hashes": [ 185 | "sha256:0107bff4f46a289f0e4081d59b77cef1c48ea43da5a0dbf0005d54748b26df2a", 186 | "sha256:07957f5471b3bb768c61f08690c96d8a09be0912185a27a68700f3ede99184e4", 187 | "sha256:10af62f87b6921eac50271e667cc234162a194e742d8e02fc4ddc121e129a5b0", 188 | "sha256:11fd60d2f69f0cefbe53ce551acf5b1cec1a89e7ce2d47b4e95a84eefb2899ae", 189 | "sha256:15e43d3b1546813669bd1a6ec7e6a11d2888db938e0607f7b5eef6b976671339", 190 | "sha256:352c24ba054a89bb9a35dd064ee95ab9b12903b56c72a8d3863d882e2632dc76", 191 | "sha256:437020a39417e85e22ea8edcb709612903a9924209e10b3ec6d8c9f05b79f498", 192 | "sha256:49925f9da7cee47eebf3420d7c0e00ec662ec6abb2780eb0a16260a7ba25f9c4", 193 | "sha256:6724fcd5777aa6cebfa7e644c526888c9d639bd22edd26b2a8038c674a7c34bd", 194 | "sha256:7a17613f7ea374ab64f39f03257f22b5755335b73251d0d253687a69029701ba", 195 | "sha256:cdc1151ced496ca1496272da7fc356580e95f2682be1d32377c22ddebdf73c91" 196 | ], 197 | "index": "pypi", 198 | "version": "==0.720" 199 | }, 200 | "mypy-extensions": { 201 | "hashes": [ 202 | "sha256:37e0e956f41369209a3d5f34580150bcacfabaa57b33a15c0b25f4b5725e0812", 203 | "sha256:b16cabe759f55e3409a7d231ebd2841378fb0c27a5d1994719e340e4f429ac3e" 204 | ], 205 | "index": "pypi", 206 | "version": "==0.4.1" 207 | }, 208 | "pycryptodome": { 209 | "hashes": [ 210 | "sha256:0281dc6a65a4d0d9e439f54e0ad5faf27bfdc2ebe9ead36912bac74a0920fa2e", 211 | "sha256:02af9b284f5c9a55f06f5e4532c16c9b7bd958e293e93969934d864ef7bd87ee", 212 | "sha256:09da99372fb69762e4b9690291176a166cc351793e2e1c9405d29ca291503aa8", 213 | "sha256:0c2400ccfc049c3f24e65d4f02bb4208d86e408011019e455fab7f50d2b226c9", 214 | "sha256:2081dd6dce6b21bf3596427edaedd4f2561dce616893b162ed2c674f3a3ca70a", 215 | "sha256:28b86ec9fdb005a2a18e4862a3a7277046738825ee8dc89cda5657e75a396089", 216 | "sha256:2d790c0d4c0d5edcf5fbab4e2af7b03757e40c5ae8d217f0dfe9ddea37fe130f", 217 | "sha256:2f24906153dca16528cf5515b1afa9ef635423d5a654904e861765f88ca667b6", 218 | "sha256:30d283939896fa4bacbdb9fa86e6fd51e9a5b953a511e210b38481f697f289f5", 219 | "sha256:31f78b67f97830d137f74813c0502a181a03b43a32ed124049bb20428176c307", 220 | "sha256:33c1f3a380fd38ab4dd4372bef17e98002b360b52814bb1b077693b1bd06ec87", 221 | "sha256:34091e9a6650c44e25339f22fc821396f19f152f65be2546edd823a093fb5a04", 222 | "sha256:567fb73951ab6865a2eb1a0060b54be1e27302574f6c65879525bdf53fab49e1", 223 | "sha256:5bc40f8aa7ba8ca7f833ad2477b9d84e1bfd2630b22a46d9bbd221982f8c3ac0", 224 | "sha256:6b0a0ccf33c7a6100c569667c888335a4aaf0d22218cb97b4963a65d70f6c343", 225 | "sha256:71b93157f1ce93fc7cfff9359b76def2b4826a7ef7a7f95e070161368e7f584a", 226 | "sha256:7d939d511b7dac29b2d936706786771ecb8256e43fade5cdb0e8bc58f02b86cf", 227 | "sha256:7fbc5a93d52e4c51487f4648b00dc41700adb144d10fc567b05f852e76c243ad", 228 | "sha256:9cb94b8f9c915a5d2b273d612a25a8e5d67b49543f8eb6bcec0275ac46cda421", 229 | "sha256:a585ea1722f9731e75881d5ffcc51d11c794d244ac57e7c2a9cbb8d5ac729302", 230 | "sha256:a6458dd7a10ae51f6fce56bdfc79bf6d3b54556237045d09e77fbda9d6d37864", 231 | "sha256:a9fb92e948128bce0239b87c6efcf2cb1c5a703d0b41dd6835211e6fafd1c5df", 232 | "sha256:b0b6b4ca1c53e7d6ca9f2720919f63837f05e7a5f92912a2bc29bfd03ed3b54f", 233 | "sha256:b7d22c8d648aaa3a7ec785eda544402141eb78ac5ffbba4cbe2c3a1f52276870", 234 | "sha256:bc9560574a868cfa2ba781b7bb0b4685b08ea251697abfc49070ffc05e1cbee6", 235 | "sha256:c0c5a576f3f7b7de3f86889cb47eb51b59dc11db9cf1e2a0f51eb4d988010ea4", 236 | "sha256:e1c91c2fa942a71c98a7a1f462de6dbbe82f34b9267eb8131314d97bd13bf0d4", 237 | "sha256:ec936361ad78aa95382c313df95777795b8185aac5dd3ec5463363ea94b556fc" 238 | ], 239 | "index": "pypi", 240 | "version": "==3.8.2" 241 | }, 242 | "pylint": { 243 | "hashes": [ 244 | "sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09", 245 | "sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1" 246 | ], 247 | "index": "pypi", 248 | "version": "==2.3.1" 249 | }, 250 | "requests": { 251 | "hashes": [ 252 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 253 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 254 | ], 255 | "index": "pypi", 256 | "version": "==2.22.0" 257 | }, 258 | "six": { 259 | "hashes": [ 260 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 261 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 262 | ], 263 | "index": "pypi", 264 | "version": "==1.12.0" 265 | }, 266 | "sympy": { 267 | "hashes": [ 268 | "sha256:71a11e5686ae7ab6cb8feb5bd2651ef4482f8fd43a7c27e645a165e4353b23e1", 269 | "sha256:f9b00ec76151c98470e84f1da2d7d03633180b71fb318428ddccce1c867d3eaa" 270 | ], 271 | "index": "pypi", 272 | "version": "==1.4" 273 | }, 274 | "toml": { 275 | "hashes": [ 276 | "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", 277 | "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" 278 | ], 279 | "index": "pypi", 280 | "version": "==0.10.0" 281 | }, 282 | "typed-ast": { 283 | "hashes": [ 284 | "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", 285 | "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", 286 | "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", 287 | "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", 288 | "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", 289 | "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", 290 | "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", 291 | "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", 292 | "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", 293 | "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", 294 | "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", 295 | "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", 296 | "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", 297 | "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", 298 | "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" 299 | ], 300 | "index": "pypi", 301 | "version": "==1.4.0" 302 | }, 303 | "typing-extensions": { 304 | "hashes": [ 305 | "sha256:2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95", 306 | "sha256:b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87", 307 | "sha256:d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed" 308 | ], 309 | "index": "pypi", 310 | "version": "==3.7.4" 311 | }, 312 | "urllib3": { 313 | "hashes": [ 314 | "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", 315 | "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" 316 | ], 317 | "index": "pypi", 318 | "version": "==1.25.3" 319 | }, 320 | "wrapt": { 321 | "hashes": [ 322 | "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" 323 | ], 324 | "index": "pypi", 325 | "version": "==1.11.2" 326 | }, 327 | "yapf": { 328 | "hashes": [ 329 | "sha256:02ace10a00fa2e36c7ebd1df2ead91dbfbd7989686dc4ccbdc549e95d19f5780", 330 | "sha256:6f94b6a176a7c114cfa6bad86d40f259bbe0f10cf2fa7f2f4b3596fc5802a41b" 331 | ], 332 | "index": "pypi", 333 | "version": "==0.28.0" 334 | } 335 | }, 336 | "develop": { 337 | "entrypoints": { 338 | "hashes": [ 339 | "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", 340 | "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451" 341 | ], 342 | "version": "==0.3" 343 | }, 344 | "flake8": { 345 | "hashes": [ 346 | "sha256:19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", 347 | "sha256:8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696" 348 | ], 349 | "index": "pypi", 350 | "version": "==3.7.8" 351 | }, 352 | "mccabe": { 353 | "hashes": [ 354 | "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", 355 | "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" 356 | ], 357 | "index": "pypi", 358 | "version": "==0.6.1" 359 | }, 360 | "mypy": { 361 | "hashes": [ 362 | "sha256:0107bff4f46a289f0e4081d59b77cef1c48ea43da5a0dbf0005d54748b26df2a", 363 | "sha256:07957f5471b3bb768c61f08690c96d8a09be0912185a27a68700f3ede99184e4", 364 | "sha256:10af62f87b6921eac50271e667cc234162a194e742d8e02fc4ddc121e129a5b0", 365 | "sha256:11fd60d2f69f0cefbe53ce551acf5b1cec1a89e7ce2d47b4e95a84eefb2899ae", 366 | "sha256:15e43d3b1546813669bd1a6ec7e6a11d2888db938e0607f7b5eef6b976671339", 367 | "sha256:352c24ba054a89bb9a35dd064ee95ab9b12903b56c72a8d3863d882e2632dc76", 368 | "sha256:437020a39417e85e22ea8edcb709612903a9924209e10b3ec6d8c9f05b79f498", 369 | "sha256:49925f9da7cee47eebf3420d7c0e00ec662ec6abb2780eb0a16260a7ba25f9c4", 370 | "sha256:6724fcd5777aa6cebfa7e644c526888c9d639bd22edd26b2a8038c674a7c34bd", 371 | "sha256:7a17613f7ea374ab64f39f03257f22b5755335b73251d0d253687a69029701ba", 372 | "sha256:cdc1151ced496ca1496272da7fc356580e95f2682be1d32377c22ddebdf73c91" 373 | ], 374 | "index": "pypi", 375 | "version": "==0.720" 376 | }, 377 | "mypy-extensions": { 378 | "hashes": [ 379 | "sha256:37e0e956f41369209a3d5f34580150bcacfabaa57b33a15c0b25f4b5725e0812", 380 | "sha256:b16cabe759f55e3409a7d231ebd2841378fb0c27a5d1994719e340e4f429ac3e" 381 | ], 382 | "index": "pypi", 383 | "version": "==0.4.1" 384 | }, 385 | "pycodestyle": { 386 | "hashes": [ 387 | "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", 388 | "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c" 389 | ], 390 | "version": "==2.5.0" 391 | }, 392 | "pyflakes": { 393 | "hashes": [ 394 | "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", 395 | "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2" 396 | ], 397 | "version": "==2.1.1" 398 | }, 399 | "typed-ast": { 400 | "hashes": [ 401 | "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", 402 | "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", 403 | "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", 404 | "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", 405 | "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", 406 | "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", 407 | "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", 408 | "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", 409 | "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", 410 | "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", 411 | "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", 412 | "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", 413 | "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", 414 | "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", 415 | "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" 416 | ], 417 | "index": "pypi", 418 | "version": "==1.4.0" 419 | }, 420 | "typing-extensions": { 421 | "hashes": [ 422 | "sha256:2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95", 423 | "sha256:b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87", 424 | "sha256:d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed" 425 | ], 426 | "index": "pypi", 427 | "version": "==3.7.4" 428 | } 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-bulletproofs 2 | Python3 implementation of Bulletproofs - non-interactive zero-knowledge proof protocol with very short proofs and without a trusted setup. 3 | 4 | Following the paper [Bulletproofs: Short Proofs for Confidential Transactions and More](https://eprint.iacr.org/2017/1066.pdf). -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.3 2 | astroid==2.2.5 3 | attrs==19.1.0 4 | black==19.3b0 5 | certifi==2019.6.16 6 | chardet==3.0.4 7 | Click==7.0 8 | codecov==2.0.15 9 | coverage==4.5.3 10 | ecdsa==0.13.2 11 | idna==2.8 12 | isort==4.3.21 13 | lazy-object-proxy==1.4.1 14 | mccabe==0.6.1 15 | mpmath==1.1.0 16 | mypy==0.720 17 | mypy-extensions==0.4.1 18 | pycryptodome==3.8.2 19 | pylint==2.3.1 20 | requests==2.22.0 21 | six==1.12.0 22 | sympy==1.4 23 | toml==0.10.0 24 | typed-ast==1.4.0 25 | typing-extensions==3.7.4 26 | urllib3==1.25.3 27 | wrapt==1.11.2 28 | yapf==0.28.0 29 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wborgeaud/python-bulletproofs/967e1a54928b0130624fba6eeb3a19b42362b83d/src/__init__.py -------------------------------------------------------------------------------- /src/innerproduct/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wborgeaud/python-bulletproofs/967e1a54928b0130624fba6eeb3a19b42362b83d/src/innerproduct/__init__.py -------------------------------------------------------------------------------- /src/innerproduct/inner_product_prover.py: -------------------------------------------------------------------------------- 1 | """Contains classes for the prover of an inner-product argument""" 2 | 3 | from typing import Optional 4 | 5 | from .inner_product_verifier import Proof1, Proof2 6 | from ..utils.commitments import vector_commitment 7 | from ..utils.utils import inner_product 8 | from ..utils.transcript import Transcript 9 | 10 | 11 | class NIProver: 12 | """Class simulating a NI prover for the inner-product argument (Protocol 1)""" 13 | def __init__(self, g, h, u, P, c, a, b, group, seed=b""): 14 | assert len(g) == len(h) == len(a) == len(b) 15 | self.g = g 16 | self.h = h 17 | self.u = u 18 | self.P = P 19 | self.c = c 20 | self.a = a 21 | self.b = b 22 | self.group = group 23 | self.transcript = Transcript(seed) 24 | 25 | def prove(self) -> Proof1: 26 | """ 27 | Proves the inner-product argument following Protocol 1 in the paper 28 | Returns a Proof1 object. 29 | """ 30 | # x = mod_hash(self.transcript.digest, self.group.order) 31 | x = self.transcript.get_modp(self.group.q) 32 | self.transcript.add_number(x) 33 | P_new = self.P + (x * self.c) * self.u 34 | u_new = x * self.u 35 | Prov2 = FastNIProver2( 36 | self.g, 37 | self.h, 38 | u_new, 39 | P_new, 40 | self.a, 41 | self.b, 42 | self.group, 43 | self.transcript.digest, 44 | ) 45 | return Proof1(u_new, P_new, Prov2.prove(), self.transcript.digest) 46 | 47 | 48 | class FastNIProver2: 49 | """Class simulating a NI prover for the inner-product argument (Protocol 2)""" 50 | def __init__(self, g, h, u, P, a, b, group, transcript: Optional[bytes]=None): 51 | assert len(g) == len(h) == len(a) == len(b) 52 | assert len(a) & (len(a) - 1) == 0 53 | self.log_n = len(a).bit_length() - 1 54 | self.n = len(a) 55 | self.g = g 56 | self.h = h 57 | self.u = u 58 | self.P = P 59 | self.a = a 60 | self.b = b 61 | self.group = group 62 | self.transcript = Transcript() 63 | if transcript: 64 | self.transcript.digest += transcript 65 | self.init_transcript_length = len(transcript.split(b"&")) 66 | else: 67 | self.init_transcript_length = 1 68 | 69 | 70 | def prove(self): 71 | """ 72 | Proves the inner-product argument following Protocol 2 in the paper 73 | Returns a Proof2 object. 74 | """ 75 | gp = self.g 76 | hp = self.h 77 | ap = self.a 78 | bp = self.b 79 | 80 | xs = [] 81 | Ls = [] 82 | Rs = [] 83 | 84 | while True: 85 | if len(ap) == len(bp) == len(gp) == len(hp) == 1: 86 | return Proof2( 87 | ap[0], 88 | bp[0], 89 | xs, 90 | Ls, 91 | Rs, 92 | self.transcript.digest, 93 | self.init_transcript_length, 94 | ) 95 | np = len(ap) // 2 96 | cl = inner_product(ap[:np], bp[np:]) 97 | cr = inner_product(ap[np:], bp[:np]) 98 | L = vector_commitment(gp[np:], hp[:np], ap[:np], bp[np:]) + cl * self.u 99 | R = vector_commitment(gp[:np], hp[np:], ap[np:], bp[:np]) + cr * self.u 100 | Ls.append(L) 101 | Rs.append(R) 102 | self.transcript.add_list_points([L, R]) 103 | # x = mod_hash(self.transcript.digest, self.group.order) 104 | x = self.transcript.get_modp(self.group.q) 105 | xs.append(x) 106 | self.transcript.add_number(x) 107 | gp = [x.inv() * gi_fh + x * gi_sh for gi_fh, gi_sh in zip(gp[:np], gp[np:])] 108 | hp = [x * hi_fh + x.inv() * hi_sh for hi_fh, hi_sh in zip(hp[:np], hp[np:])] 109 | ap = [x * ai_fh + x.inv() * ai_sh for ai_fh, ai_sh in zip(ap[:np], ap[np:])] 110 | bp = [x.inv() * bi_fh + x * bi_sh for bi_fh, bi_sh in zip(bp[:np], bp[np:])] 111 | -------------------------------------------------------------------------------- /src/innerproduct/inner_product_verifier.py: -------------------------------------------------------------------------------- 1 | """Contains classes for the prover of an inner-product argument""" 2 | 3 | from fastecdsa.curve import secp256k1, Curve 4 | from ..utils.utils import mod_hash, point_to_b64, ModP 5 | from ..pippenger import PipSECP256k1 6 | 7 | SUPERCURVE: Curve = secp256k1 8 | 9 | 10 | class Proof1: 11 | """Proof class for Protocol 1""" 12 | 13 | def __init__(self, u_new, P_new, proof2, transcript): 14 | self.u_new = u_new 15 | self.P_new = P_new 16 | self.proof2 = proof2 17 | self.transcript = transcript 18 | 19 | 20 | class Verifier1: 21 | """Verifier class for Protocol 1""" 22 | 23 | def __init__(self, g, h, u, P, c, proof1): 24 | self.g = g 25 | self.h = h 26 | self.u = u 27 | self.P = P 28 | self.c = c 29 | self.proof1 = proof1 30 | 31 | def assertThat(self, expr: bool): 32 | """Assert that expr is truthy else raise exception""" 33 | if not expr: 34 | raise Exception("Proof invalid") 35 | 36 | def verify_transcript(self): 37 | """Verify a transcript to assure Fiat-Shamir was done properly""" 38 | lTranscript = self.proof1.transcript.split(b"&") 39 | self.assertThat( 40 | lTranscript[1] 41 | == str(mod_hash(b"&".join(lTranscript[:1]) + b"&", SUPERCURVE.q)).encode() 42 | ) 43 | 44 | def verify(self): 45 | """Verifies the proof given by a prover. Raises an execption if it is invalid""" 46 | self.verify_transcript() 47 | 48 | lTranscript = self.proof1.transcript.split(b"&") 49 | x = lTranscript[1] 50 | x = ModP(int(x), SUPERCURVE.q) 51 | self.assertThat(self.proof1.P_new == self.P + (x * self.c) * self.u) 52 | self.assertThat(self.proof1.u_new == x * self.u) 53 | 54 | Verif2 = Verifier2( 55 | self.g, self.h, self.proof1.u_new, self.proof1.P_new, self.proof1.proof2 56 | ) 57 | 58 | return Verif2.verify() 59 | 60 | 61 | class Proof2: 62 | """Proof class for Protocol 2""" 63 | 64 | def __init__(self, a, b, xs, Ls, Rs, transcript, start_transcript: int = 0): 65 | self.a = a 66 | self.b = b 67 | self.xs = xs 68 | self.Ls = Ls 69 | self.Rs = Rs 70 | self.transcript = transcript 71 | self.start_transcript = ( 72 | start_transcript 73 | ) # Start of transcript to be used if Protocol 2 is run in Protocol 1 74 | 75 | 76 | class Verifier2: 77 | """Verifier class for Protocol 2""" 78 | 79 | def __init__(self, g, h, u, P, proof: Proof2): 80 | self.g = g 81 | self.h = h 82 | self.u = u 83 | self.P = P 84 | self.proof = proof 85 | 86 | def assertThat(self, expr): 87 | """Assert that expr is truthy else raise exception""" 88 | if not expr: 89 | raise Exception("Proof invalid") 90 | 91 | def get_ss(self, xs): 92 | """See page 15 in paper""" 93 | n = len(self.g) 94 | log_n = n.bit_length() - 1 95 | ss = [] 96 | for i in range(1, n + 1): 97 | tmp = ModP(1, SUPERCURVE.q) 98 | for j in range(0, log_n): 99 | b = 1 if bin(i - 1)[2:].zfill(log_n)[j] == "1" else -1 100 | tmp *= xs[j] if b == 1 else xs[j].inv() 101 | ss.append(tmp) 102 | return ss 103 | 104 | def verify_transcript(self): 105 | """Verify a transcript to assure Fiat-Shamir was done properly""" 106 | init_len = self.proof.start_transcript 107 | n = len(self.g) 108 | log_n = n.bit_length() - 1 109 | Ls = self.proof.Ls 110 | Rs = self.proof.Rs 111 | xs = self.proof.xs 112 | lTranscript = self.proof.transcript.split(b"&") 113 | for i in range(log_n): 114 | self.assertThat(lTranscript[init_len + i * 3] == point_to_b64(Ls[i])) 115 | self.assertThat(lTranscript[init_len + i * 3 + 1] == point_to_b64(Rs[i])) 116 | self.assertThat( 117 | str(xs[i]).encode() 118 | == lTranscript[init_len + i * 3 + 2] 119 | == str( 120 | mod_hash( 121 | b"&".join(lTranscript[: init_len + i * 3 + 2]) + b"&", 122 | SUPERCURVE.q, 123 | ) 124 | ).encode() 125 | ) 126 | 127 | def verify(self): 128 | """Verifies the proof given by a prover. Raises an execption if it is invalid""" 129 | self.verify_transcript() 130 | 131 | proof = self.proof 132 | Pip = PipSECP256k1 133 | ss = self.get_ss(self.proof.xs) 134 | LHS = Pip.multiexp( 135 | self.g + self.h + [self.u], 136 | [proof.a * ssi for ssi in ss] 137 | + [proof.b * ssi.inv() for ssi in ss] 138 | + [proof.a * proof.b], 139 | ) 140 | RHS = self.P + Pip.multiexp( 141 | proof.Ls + proof.Rs, 142 | [xi ** 2 for xi in proof.xs] + [xi.inv() ** 2 for xi in proof.xs], 143 | ) 144 | 145 | self.assertThat(LHS == RHS) 146 | print("OK") 147 | return True 148 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | """Various tests""" 2 | 3 | from fastecdsa.curve import secp256k1 4 | import os 5 | from .utils.utils import mod_hash, inner_product, ModP 6 | from .utils.commitments import vector_commitment, commitment 7 | from .utils.elliptic_curve_hash import elliptic_hash 8 | 9 | # from .rangeproofs.rangeproof_prover import NIRangeProver 10 | # from .rangeproofs.rangeproof_verifier import RangeVerifier 11 | from .rangeproofs.rangeproof_aggreg_prover import AggregNIRangeProver 12 | from .rangeproofs.rangeproof_aggreg_verifier import AggregRangeVerifier 13 | 14 | 15 | CURVE = secp256k1 16 | p = CURVE.q 17 | 18 | # seeds = [os.urandom(10) for _ in range(7)] 19 | # v, n = ModP(15,p), 16 20 | # gs = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n)] 21 | # hs = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n)] 22 | # g = elliptic_hash(seeds[2], CURVE) 23 | # h = elliptic_hash(seeds[3], CURVE) 24 | # u = elliptic_hash(seeds[4], CURVE) 25 | # gamma = mod_hash(seeds[5],p) 26 | 27 | # V = commitment(g,h,v,gamma) 28 | 29 | 30 | # Prov = NIRangeProver(v,n,g,h,gs,hs,gamma,u,SUPERCURVE,seeds[6]) 31 | # proof = Prov.prove() 32 | # Verif = RangeVerifier(V,g,h,gs,hs,u,proof) 33 | # Verif.verify() 34 | m = 4 35 | seeds = [os.urandom(10) for _ in range(7)] 36 | vs, n = [ModP(15, p) for _ in range(m)], 16 37 | vs[-1] = ModP(2 ** 16 - 1, p) 38 | gs = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n * m)] 39 | hs = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n * m)] 40 | g = elliptic_hash(seeds[2], CURVE) 41 | h = elliptic_hash(seeds[3], CURVE) 42 | u = elliptic_hash(seeds[4], CURVE) 43 | gammas = [mod_hash(seeds[5], p) for _ in range(m)] 44 | 45 | Vs = [commitment(g, h, vs[i], gammas[i]) for i in range(m)] 46 | 47 | 48 | Prov = AggregNIRangeProver(vs, n, g, h, gs, hs, gammas, u, CURVE, seeds[6]) 49 | proof = Prov.prove() 50 | Verif = AggregRangeVerifier(Vs, g, h, gs, hs, u, proof) 51 | Verif.verify() 52 | -------------------------------------------------------------------------------- /src/pippenger/__init__.py: -------------------------------------------------------------------------------- 1 | from fastecdsa.curve import secp256k1 2 | from .pippenger import Pippenger 3 | from .group import EC 4 | 5 | PipSECP256k1 = Pippenger(EC(secp256k1)) 6 | 7 | __all__ = ["Pippenger", "EC", "PipSECP256k1"] -------------------------------------------------------------------------------- /src/pippenger/group.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from .modp import ModP 3 | from fastecdsa.curve import Curve 4 | 5 | 6 | class Group(ABC): 7 | def __init__(self, unit, order): 8 | self.unit = unit 9 | self.order = order 10 | 11 | @abstractmethod 12 | def mult(self, x, y): 13 | pass 14 | 15 | def square(self, x): 16 | return self.mult(x, x) 17 | 18 | 19 | class MultIntModP(Group): 20 | def __init__(self, p, order): 21 | Group.__init__(self, ModP(1, p), order) 22 | 23 | def mult(self, x, y): 24 | return x * y 25 | 26 | 27 | class EC(Group): 28 | def __init__(self, curve: Curve): 29 | Group.__init__(self, curve.G.IDENTITY_ELEMENT, curve.q) 30 | 31 | def mult(self, x, y): 32 | return x + y 33 | -------------------------------------------------------------------------------- /src/pippenger/modp.py: -------------------------------------------------------------------------------- 1 | class ModP: 2 | 3 | num_of_mult=0 4 | @classmethod 5 | def reset(cls): 6 | cls.num_of_mult = 0 7 | 8 | def __init__(self, x, p): 9 | self.x = x 10 | self.p = p 11 | 12 | def __add__(self, y): 13 | if isinstance(y, int): 14 | return ModP(self.x+y, self.p) 15 | assert self.p == y.p 16 | return ModP((self.x + y.x) % self.p, self.p) 17 | 18 | def __mul__(self, y): 19 | type(self).num_of_mult += 1 20 | if isinstance(y, int): 21 | return ModP(self.x*y, self.p) 22 | assert self.p == y.p 23 | return ModP((self.x * y.x) % self.p, self.p) 24 | 25 | def __sub__(self, y): 26 | if isinstance(y, int): 27 | return ModP(self.x-y, self.p) 28 | assert self.p == y.p 29 | return ModP((self.x - y.x) % self.p, self.p) 30 | 31 | def __pow__(self, n): 32 | # return ModP(pow(self.x, n, self.p), self.p) 33 | exp = bin(n) 34 | value = ModP(self.x, self.p) 35 | 36 | for i in range(3, len(exp)): 37 | value = value * value 38 | if(exp[i:i+1]=='1'): 39 | value = value*self 40 | return value 41 | 42 | 43 | def __neg__(self): 44 | return ModP(self.p - self.x, self.p) 45 | 46 | def __eq__(self, y): 47 | return (self.x == y.x) and (self.p == y.p) 48 | 49 | 50 | def __str__(self): 51 | return str(self.x) 52 | def __repr__(self): 53 | return str(self.x) -------------------------------------------------------------------------------- /src/pippenger/pippenger.py: -------------------------------------------------------------------------------- 1 | from sympy import integer_nthroot 2 | from math import log2, floor 3 | from itertools import combinations 4 | 5 | def subset_of(l): 6 | return sum(map(lambda r: list(combinations(l, r)), range(1, len(l)+1)), []) 7 | 8 | class Pippenger: 9 | def __init__(self, group): 10 | self.G = group 11 | self.order = group.order 12 | self.lamb = group.order.bit_length() 13 | 14 | # Returns g^(2^j) 15 | def _pow2powof2(self, g, j): 16 | tmp = g 17 | for _ in range(j): 18 | tmp = self.G.square(tmp) 19 | return tmp 20 | 21 | # Returns Prod g_i ^ e_i 22 | def multiexp(self, gs, es): 23 | if len(gs) != len(es): 24 | raise Exception('Different number of group elements and exponents') 25 | 26 | es = [ei%self.G.order for ei in es] 27 | 28 | if len(gs) == 0: 29 | return self.G.unit 30 | 31 | lamb = self.lamb 32 | N = len(gs) 33 | s = integer_nthroot(lamb//N, 2)[0]+1 34 | t = integer_nthroot(lamb*N,2)[0]+1 35 | gs_bin = [] 36 | for i in range(N): 37 | tmp = [gs[i]] 38 | for j in range(1,s): 39 | tmp.append(self.G.square(tmp[-1])) 40 | gs_bin.append(tmp) 41 | es_bin = [] 42 | for i in range(N): 43 | tmp1 = [] 44 | for j in range(s): 45 | tmp2 = [] 46 | for k in range(t): 47 | tmp2.append(int( bin(es[i])[2:].zfill(s*t)[-(j+s*k+1)]) ) 48 | tmp1.append(tmp2) 49 | es_bin.append(tmp1) 50 | 51 | Gs = self._multiexp_bin( 52 | [gs_bin[i][j] for i in range(N) for j in range(s)], 53 | [es_bin[i][j] for i in range(N) for j in range(s)] 54 | ) 55 | 56 | ans2 = Gs[-1] 57 | for k in range(len(Gs)-2,-1,-1): 58 | ans2 = self._pow2powof2(ans2, s) 59 | ans2 = self.G.mult(ans2, Gs[k]) 60 | 61 | return ans2 62 | 63 | def _multiexp_bin(self, gs, es): 64 | assert len(gs) == len(es) 65 | M = len(gs) 66 | b = floor( log2(M) - log2(log2(M)) ) 67 | b = b if b else 1 68 | subsets = [list(range(i,min(i+b,M))) for i in range(0,M,b)] 69 | Ts = [{sub: None for sub in subset_of(S)} for S in subsets] 70 | 71 | for T,S in zip(Ts, subsets): 72 | for i in S: 73 | T[(i,)] = gs[i] 74 | # Recursively set the subproducts in T 75 | def set_sub(sub): 76 | if T[sub] is None: 77 | if T[sub[:-1]] is None: 78 | set_sub(sub[:-1]) 79 | T[sub] = self.G.mult(T[sub[:-1]], gs[sub[-1]]) 80 | for sub in T: 81 | set_sub(sub) 82 | 83 | Gs = [] 84 | for k in range(len(es[0])): 85 | tmp = self.G.unit 86 | for T,S in zip(Ts, subsets): 87 | sub_es = [j for j in S if es[j][k]] 88 | sub_es = tuple(sub_es) 89 | if not sub_es: 90 | continue 91 | tmp = self.G.mult(tmp, T[sub_es]) 92 | Gs.append(tmp) 93 | 94 | return Gs 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/rangeproofs/__init__.py: -------------------------------------------------------------------------------- 1 | from .rangeproof_prover import NIRangeProver 2 | from .rangeproof_verifier import RangeVerifier 3 | from .rangeproof_aggreg_prover import AggregNIRangeProver 4 | from .rangeproof_aggreg_verifier import AggregRangeVerifier 5 | 6 | __all__ = [ 7 | "NIRangeProver", 8 | "RangeVerifier", 9 | "AggregNIRangeProver", 10 | "AggregRangeVerifier", 11 | ] 12 | -------------------------------------------------------------------------------- /src/rangeproofs/rangeproof_aggreg_prover.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from ..utils.utils import Point, ModP, inner_product, mod_hash 3 | from ..utils.transcript import Transcript 4 | from ..utils.commitments import vector_commitment, commitment 5 | from .rangeproof_verifier import Proof 6 | from ..innerproduct.inner_product_prover import NIProver 7 | from ..pippenger import PipSECP256k1 8 | 9 | 10 | class AggregNIRangeProver: 11 | def __init__( 12 | self, 13 | vs: List[ModP], 14 | n: int, 15 | g: Point, 16 | h: Point, 17 | gs: List[Point], 18 | hs: List[Point], 19 | gammas: List[ModP], 20 | u: Point, 21 | group, 22 | seed: bytes = b"", 23 | ): 24 | self.vs = vs 25 | self.n = n 26 | self.g = g 27 | self.h = h 28 | self.gs = gs 29 | self.hs = hs 30 | self.gammas = gammas 31 | self.u = u 32 | self.group = group 33 | self.transcript = Transcript(seed) 34 | self.m = len(vs) 35 | 36 | def prove(self): 37 | vs = self.vs 38 | n = self.n 39 | m = self.m 40 | gs = self.gs 41 | hs = self.hs 42 | h = self.h 43 | 44 | aL = [] 45 | for v in vs: 46 | aL += list(map(int, reversed(bin(v.x)[2:].zfill(n))))[:n] 47 | aR = [ 48 | (x - 1) % self.group.q for x in aL 49 | ] # TODO implement inverse of elliptic curve point to compute -1 * g instead of multiplying by p-1 50 | 51 | alpha = mod_hash(b"alpha" + self.transcript.digest, self.group.q) 52 | A = vector_commitment(gs, hs, aL, aR) + alpha * h 53 | sL = [ 54 | mod_hash(str(i).encode() + self.transcript.digest, self.group.q) 55 | for i in range(n * m) 56 | ] 57 | sR = [ 58 | mod_hash(str(i).encode() + self.transcript.digest, self.group.q) 59 | for i in range(n * m, 2 * n * m) 60 | ] 61 | rho = mod_hash(str(2 * n).encode() + self.transcript.digest, self.group.q) 62 | S = vector_commitment(gs, hs, sL, sR) + rho * h 63 | self.transcript.add_list_points([A, S]) 64 | y = self.transcript.get_modp(self.group.q) 65 | self.transcript.add_number(y) 66 | z = self.transcript.get_modp(self.group.q) 67 | self.transcript.add_number(z) 68 | 69 | t1, t2 = self._get_polynomial_coeffs(aL, aR, sL, sR, y, z) 70 | tau1 = mod_hash(b"tau1" + self.transcript.digest, self.group.q) 71 | tau2 = mod_hash(b"tau2" + self.transcript.digest, self.group.q) 72 | T1 = commitment(self.g, h, t1, tau1) 73 | T2 = commitment(self.g, h, t2, tau2) 74 | self.transcript.add_list_points([T1, T2]) 75 | x = self.transcript.get_modp(self.group.q) 76 | self.transcript.add_number(x) 77 | taux, mu, t_hat, ls, rs = self._final_compute( 78 | aL, aR, sL, sR, y, z, x, tau1, tau2, alpha, rho 79 | ) 80 | 81 | # return Proof(taux, mu, t_hat, ls, rs, T1, T2, A, S), x,y,z 82 | hsp = [(y.inv() ** i) * hs[i] for i in range(n * m)] 83 | # P = ( 84 | # A 85 | # + x * S 86 | # + sum([(-z) * gs[i] for i in range(n*m)], Point(None, None, None)) 87 | # + sum( 88 | # [((z * (y ** i)) + (z ** (2 + (i // self.n))) * (2 ** (i % self.n))) * hsp[i] for i in range(n*m)], 89 | # Point(None, None, None), 90 | # ) 91 | # ) 92 | P = ( 93 | A 94 | + x * S 95 | + PipSECP256k1.multiexp( 96 | gs + hsp, 97 | [-z for _ in range(n * m)] 98 | + [ 99 | (z * (y ** i)) + (z ** (2 + (i // self.n))) * (2 ** (i % self.n)) 100 | for i in range(n * m) 101 | ], 102 | ) 103 | ) 104 | InnerProv = NIProver(gs, hsp, self.u, P + (-mu) * h, t_hat, ls, rs, self.group) 105 | innerProof = InnerProv.prove() 106 | 107 | ### DEBUG ### 108 | delta_yz = (z - z ** 2) * sum( 109 | [y ** i for i in range(n * m)], ModP(0, self.group.q) 110 | ) - sum( 111 | [(z ** (j + 2)) * ModP(2 ** n - 1, self.group.q) for j in range(1, m + 1)] 112 | ) 113 | t0 = sum([vs[j] * (z ** (2 + j)) for j in range(m)]) + delta_yz 114 | ### DEBUG ### 115 | return Proof(taux, mu, t_hat, T1, T2, A, S, innerProof, self.transcript.digest) 116 | 117 | def _get_polynomial_coeffs(self, aL, aR, sL, sR, y, z): 118 | t1 = inner_product( 119 | sL, 120 | [ 121 | (y ** i) * (aR[i] + z) 122 | + (z ** (2 + (i // self.n))) * (2 ** (i % self.n)) 123 | for i in range(self.n * self.m) 124 | ], 125 | ) + inner_product( 126 | [aL[i] - z for i in range(self.n * self.m)], 127 | [(y ** i) * sR[i] for i in range(self.n * self.m)], 128 | ) 129 | t2 = inner_product(sL, [(y ** i) * sR[i] for i in range(self.n * self.m)]) 130 | return t1, t2 131 | 132 | def _final_compute(self, aL, aR, sL, sR, y, z, x, tau1, tau2, alpha, rho): 133 | ls = [aL[i] - z + sL[i] * x for i in range(self.n * self.m)] 134 | rs = [ 135 | (y ** i) * (aR[i] + z + sR[i] * x) 136 | + (z ** (2 + (i // self.n))) * (2 ** (i % self.n)) 137 | for i in range(self.n * self.m) 138 | ] 139 | t_hat = inner_product(ls, rs) 140 | taux = ( 141 | tau2 * (x ** 2) 142 | + tau1 * x 143 | + sum([(z ** (2 + j)) * self.gammas[j] for j in range(self.m)]) 144 | ) 145 | mu = alpha + rho * x 146 | return taux, mu, t_hat, ls, rs 147 | -------------------------------------------------------------------------------- /src/rangeproofs/rangeproof_aggreg_verifier.py: -------------------------------------------------------------------------------- 1 | from fastecdsa.curve import secp256k1 2 | 3 | from ..utils.utils import ModP, point_to_b64 4 | from ..innerproduct.inner_product_verifier import Verifier1 5 | from ..pippenger import PipSECP256k1 6 | 7 | CURVE = secp256k1 8 | 9 | 10 | class Proof: 11 | """Proof class for Protocol 1""" 12 | 13 | def __init__(self, taux, mu, t_hat, T1, T2, A, S, innerProof, transcript): 14 | self.taux = taux 15 | self.mu = mu 16 | self.t_hat = t_hat 17 | self.T1 = T1 18 | self.T2 = T2 19 | self.A = A 20 | self.S = S 21 | self.innerProof = innerProof 22 | self.transcript = transcript 23 | 24 | 25 | class AggregRangeVerifier: 26 | """Verifier class for Range Proofs""" 27 | 28 | def __init__(self, Vs, g, h, gs, hs, u, proof: Proof): 29 | self.Vs = Vs 30 | self.g = g 31 | self.h = h 32 | self.gs = gs 33 | self.hs = hs 34 | self.u = u 35 | self.proof = proof 36 | 37 | def assertThat(self, expr: bool): 38 | """Assert that expr is truthy else raise exception""" 39 | if not expr: 40 | raise Exception("Proof invalid") 41 | 42 | def verify_transcript(self): 43 | """Verify a transcript to assure Fiat-Shamir was done properly""" 44 | proof = self.proof 45 | p = proof.taux.p 46 | lTranscript = proof.transcript.split(b"&") 47 | self.assertThat(lTranscript[1] == point_to_b64(proof.A)) 48 | self.assertThat(lTranscript[2] == point_to_b64(proof.S)) 49 | self.y = ModP(int(lTranscript[3]), p) 50 | self.z = ModP(int(lTranscript[4]), p) 51 | self.assertThat(lTranscript[5] == point_to_b64(proof.T1)) 52 | self.assertThat(lTranscript[6] == point_to_b64(proof.T2)) 53 | self.x = ModP(int(lTranscript[7]), p) 54 | 55 | def verify(self): 56 | """Verifies the proof given by a prover. Raises an execption if it is invalid""" 57 | self.verify_transcript() 58 | 59 | g = self.g 60 | h = self.h 61 | gs = self.gs 62 | hs = self.hs 63 | x = self.x 64 | y = self.y 65 | z = self.z 66 | proof = self.proof 67 | 68 | nm = len(gs) 69 | m = len(self.Vs) 70 | n = nm // m 71 | 72 | delta_yz = (z - z ** 2) * sum( 73 | [y ** i for i in range(nm)], ModP(0, CURVE.q) 74 | ) - sum( 75 | [ 76 | (z ** (j + 2)) * ModP(2 ** n - 1, CURVE.q) 77 | for j in range(1, m + 1) 78 | ] 79 | ) 80 | hsp = [(y.inv() ** i) * hs[i] for i in range(nm)] 81 | 82 | self.assertThat( 83 | proof.t_hat * g + proof.taux * h 84 | == PipSECP256k1.multiexp( 85 | self.Vs + [g, proof.T1, proof.T2], 86 | [z ** (j + 2) for j in range(m)] + [delta_yz, x, x ** 2], 87 | ) 88 | ) 89 | 90 | P = self._getP(x, y, z, proof.A, proof.S, gs, hsp, n, m) 91 | InnerVerif = Verifier1( 92 | gs, hsp, self.u, P + (-proof.mu) * h, proof.t_hat, proof.innerProof 93 | ) 94 | return InnerVerif.verify() 95 | 96 | def _getP(self, x, y, z, A, S, gs, hsp, n, m): 97 | return ( 98 | A 99 | + x * S 100 | + PipSECP256k1.multiexp( 101 | gs + hsp, 102 | [-z for _ in range(n * m)] 103 | + [ 104 | (z * (y ** i)) + (z ** (2 + (i // n))) * (2 ** (i % n)) 105 | for i in range(n * m) 106 | ], 107 | ) 108 | ) 109 | -------------------------------------------------------------------------------- /src/rangeproofs/rangeproof_prover.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from ..utils.utils import Point, ModP, inner_product, mod_hash 3 | from ..utils.transcript import Transcript 4 | from ..utils.commitments import vector_commitment, commitment 5 | from .rangeproof_verifier import Proof 6 | from ..innerproduct.inner_product_prover import NIProver 7 | from ..pippenger import PipSECP256k1 8 | 9 | 10 | class NIRangeProver: 11 | def __init__( 12 | self, 13 | v: ModP, 14 | n: int, 15 | g: Point, 16 | h: Point, 17 | gs: List[Point], 18 | hs: List[Point], 19 | gamma: ModP, 20 | u: Point, 21 | group, 22 | seed: bytes = b"", 23 | ): 24 | self.v = v 25 | self.n = n 26 | self.g = g 27 | self.h = h 28 | self.gs = gs 29 | self.hs = hs 30 | self.gamma = gamma 31 | self.u = u 32 | self.group = group 33 | self.transcript = Transcript(seed) 34 | 35 | def prove(self): 36 | v = self.v 37 | n = self.n 38 | gs = self.gs 39 | hs = self.hs 40 | h = self.h 41 | 42 | aL = list(map(int, reversed(bin(v.x)[2:].zfill(n))))[:n] 43 | aR = [ 44 | (x - 1) % self.group.q for x in aL 45 | ] # TODO implement inverse of elliptic curve point to compute -1 * g instead of multiplying by p-1 46 | alpha = mod_hash(b"alpha" + self.transcript.digest, self.group.q) 47 | A = vector_commitment(gs, hs, aL, aR) + alpha * h 48 | sL = [ 49 | mod_hash(str(i).encode() + self.transcript.digest, self.group.q) 50 | for i in range(n) 51 | ] 52 | sR = [ 53 | mod_hash(str(i).encode() + self.transcript.digest, self.group.q) 54 | for i in range(n, 2 * n) 55 | ] 56 | rho = mod_hash(str(2 * n).encode() + self.transcript.digest, self.group.q) 57 | S = vector_commitment(gs, hs, sL, sR) + rho * h 58 | self.transcript.add_list_points([A, S]) 59 | y = self.transcript.get_modp(self.group.q) 60 | self.transcript.add_number(y) 61 | z = self.transcript.get_modp(self.group.q) 62 | self.transcript.add_number(z) 63 | 64 | t1, t2 = self._get_polynomial_coeffs(aL, aR, sL, sR, y, z) 65 | tau1 = mod_hash(b"tau1" + self.transcript.digest, self.group.q) 66 | tau2 = mod_hash(b"tau2" + self.transcript.digest, self.group.q) 67 | T1 = commitment(self.g, h, t1, tau1) 68 | T2 = commitment(self.g, h, t2, tau2) 69 | self.transcript.add_list_points([T1, T2]) 70 | x = self.transcript.get_modp(self.group.q) 71 | self.transcript.add_number(x) 72 | taux, mu, t_hat, ls, rs = self._final_compute( 73 | aL, aR, sL, sR, y, z, x, tau1, tau2, alpha, rho 74 | ) 75 | 76 | # return Proof(taux, mu, t_hat, ls, rs, T1, T2, A, S), x,y,z 77 | hsp = [(y.inv() ** i) * hs[i] for i in range(n)] 78 | P = ( 79 | A 80 | + x * S 81 | + PipSECP256k1.multiexp( 82 | gs + hsp, 83 | [-z for _ in range(n)] 84 | + [(z * (y ** i)) + ((z ** 2) * (2 ** i)) for i in range(n)], 85 | ) 86 | ) 87 | 88 | InnerProv = NIProver(gs, hsp, self.u, P + (-mu) * h, t_hat, ls, rs, self.group) 89 | innerProof = InnerProv.prove() 90 | 91 | return Proof(taux, mu, t_hat, T1, T2, A, S, innerProof, self.transcript.digest) 92 | 93 | def _get_polynomial_coeffs(self, aL, aR, sL, sR, y, z): 94 | t1 = inner_product( 95 | sL, [(y ** i) * (aR[i] + z) + (z ** 2) * (2 ** i) for i in range(self.n)] 96 | ) + inner_product( 97 | [aL[i] - z for i in range(self.n)], 98 | [(y ** i) * sR[i] for i in range(self.n)], 99 | ) 100 | t2 = inner_product(sL, [(y ** i) * sR[i] for i in range(self.n)]) 101 | return t1, t2 102 | 103 | def _final_compute(self, aL, aR, sL, sR, y, z, x, tau1, tau2, alpha, rho): 104 | ls = [aL[i] - z + sL[i] * x for i in range(self.n)] 105 | rs = [ 106 | (y ** i) * (aR[i] + z + sR[i] * x) + (z ** 2) * (2 ** i) 107 | for i in range(self.n) 108 | ] 109 | t_hat = inner_product(ls, rs) 110 | taux = tau2 * (x ** 2) + tau1 * x + (z ** 2) * self.gamma 111 | mu = alpha + rho * x 112 | return taux, mu, t_hat, ls, rs 113 | -------------------------------------------------------------------------------- /src/rangeproofs/rangeproof_verifier.py: -------------------------------------------------------------------------------- 1 | from fastecdsa.curve import secp256k1 2 | 3 | from ..utils.utils import ModP, point_to_b64 4 | from ..innerproduct.inner_product_verifier import Verifier1 5 | from ..pippenger import PipSECP256k1 6 | 7 | CURVE = secp256k1 8 | 9 | 10 | class Proof: 11 | """Proof class for Protocol 1""" 12 | 13 | def __init__(self, taux, mu, t_hat, T1, T2, A, S, innerProof, transcript): 14 | self.taux = taux 15 | self.mu = mu 16 | self.t_hat = t_hat 17 | self.T1 = T1 18 | self.T2 = T2 19 | self.A = A 20 | self.S = S 21 | self.innerProof = innerProof 22 | self.transcript = transcript 23 | 24 | 25 | class RangeVerifier: 26 | """Verifier class for Range Proofs""" 27 | 28 | def __init__(self, V, g, h, gs, hs, u, proof: Proof): 29 | self.V = V 30 | self.g = g 31 | self.h = h 32 | self.gs = gs 33 | self.hs = hs 34 | self.u = u 35 | self.proof = proof 36 | 37 | def assertThat(self, expr: bool): 38 | """Assert that expr is truthy else raise exception""" 39 | if not expr: 40 | raise Exception("Proof invalid") 41 | 42 | def verify_transcript(self): 43 | """Verify a transcript to assure Fiat-Shamir was done properly""" 44 | proof = self.proof 45 | p = proof.taux.p 46 | lTranscript = proof.transcript.split(b"&") 47 | self.assertThat(lTranscript[1] == point_to_b64(proof.A)) 48 | self.assertThat(lTranscript[2] == point_to_b64(proof.S)) 49 | self.y = ModP(int(lTranscript[3]), p) 50 | self.z = ModP(int(lTranscript[4]), p) 51 | self.assertThat(lTranscript[5] == point_to_b64(proof.T1)) 52 | self.assertThat(lTranscript[6] == point_to_b64(proof.T2)) 53 | self.x = ModP(int(lTranscript[7]), p) 54 | 55 | def verify(self): 56 | """Verifies the proof given by a prover. Raises an execption if it is invalid""" 57 | self.verify_transcript() 58 | 59 | g = self.g 60 | h = self.h 61 | gs = self.gs 62 | hs = self.hs 63 | x = self.x 64 | y = self.y 65 | z = self.z 66 | proof = self.proof 67 | 68 | n = len(gs) 69 | delta_yz = (z - z ** 2) * sum( 70 | [y ** i for i in range(n)], ModP(0, CURVE.q) 71 | ) - (z ** 3) * ModP(2 ** n - 1, CURVE.q) 72 | hsp = [(y.inv() ** i) * hs[i] for i in range(n)] 73 | self.assertThat( 74 | proof.t_hat * g + proof.taux * h 75 | == (z ** 2) * self.V + delta_yz * g + x * proof.T1 + (x ** 2) * proof.T2 76 | ) 77 | 78 | P = self._getP(x, y, z, proof.A, proof.S, gs, hsp, n) 79 | # self.assertThat( 80 | # P == vector_commitment(gs, hsp, proof.ls, proof.rs) + proof.mu * h 81 | # ) 82 | # self.assertThat(proof.t_hat == inner_product(proof.ls, proof.rs)) 83 | InnerVerif = Verifier1( 84 | gs, hsp, self.u, P + (-proof.mu) * h, proof.t_hat, proof.innerProof 85 | ) 86 | return InnerVerif.verify() 87 | 88 | def _getP(self, x, y, z, A, S, gs, hsp, n): 89 | return ( 90 | A 91 | + x * S 92 | + PipSECP256k1.multiexp( 93 | gs + hsp, 94 | [-z for _ in range(n)] 95 | + [(z * (y ** i)) + ((z ** 2) * (2 ** i)) for i in range(n)], 96 | ) 97 | ) 98 | -------------------------------------------------------------------------------- /src/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wborgeaud/python-bulletproofs/967e1a54928b0130624fba6eeb3a19b42362b83d/src/tests/__init__.py -------------------------------------------------------------------------------- /src/tests/test_aggreg_rangeproofs.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | from random import randint 4 | from fastecdsa.curve import secp256k1 5 | from ..utils.commitments import commitment 6 | from ..utils.utils import mod_hash, ModP 7 | from ..utils.elliptic_curve_hash import elliptic_hash 8 | from ..rangeproofs import AggregNIRangeProver, AggregRangeVerifier 9 | 10 | 11 | CURVE = secp256k1 12 | p = secp256k1.q 13 | 14 | 15 | class AggregRangeProofTest(unittest.TestCase): 16 | def test_different_seeds(self): 17 | for _ in range(10): 18 | m = 4 19 | seeds = [os.urandom(10) for _ in range(7)] 20 | vs, n = [ModP(randint(0, 2 ** 16 - 1), p) for _ in range(m)], 16 21 | gs = [ 22 | elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n * m) 23 | ] 24 | hs = [ 25 | elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n * m) 26 | ] 27 | g = elliptic_hash(seeds[2], CURVE) 28 | h = elliptic_hash(seeds[3], CURVE) 29 | u = elliptic_hash(seeds[4], CURVE) 30 | gammas = [mod_hash(seeds[5], p) for _ in range(m)] 31 | Vs = [commitment(g, h, vs[i], gammas[i]) for i in range(m)] 32 | Prov = AggregNIRangeProver( 33 | vs, n, g, h, gs, hs, gammas, u, CURVE, seeds[6] 34 | ) 35 | proof = Prov.prove() 36 | Verif = AggregRangeVerifier(Vs, g, h, gs, hs, u, proof) 37 | with self.subTest(seeds=seeds, vs=vs): 38 | self.assertTrue(Verif.verify()) 39 | 40 | def test_different_n_and_vs(self): 41 | for _ in range(3): 42 | for i in range(1, 8): 43 | m = 4 44 | seeds = [os.urandom(10) for _ in range(7)] 45 | vs, n = ( 46 | [ModP(randint(0, 2 ** (2 ** i) - 1), p) for _ in range(m)], 47 | 2 ** i, 48 | ) 49 | gs = [ 50 | elliptic_hash(str(i).encode() + seeds[0], CURVE) 51 | for i in range(n * m) 52 | ] 53 | hs = [ 54 | elliptic_hash(str(i).encode() + seeds[1], CURVE) 55 | for i in range(n * m) 56 | ] 57 | g = elliptic_hash(seeds[2], CURVE) 58 | h = elliptic_hash(seeds[3], CURVE) 59 | u = elliptic_hash(seeds[4], CURVE) 60 | gammas = [mod_hash(seeds[5], p) for _ in range(m)] 61 | Vs = [commitment(g, h, vs[i], gammas[i]) for i in range(m)] 62 | Prov = AggregNIRangeProver( 63 | vs, n, g, h, gs, hs, gammas, u, CURVE, seeds[6] 64 | ) 65 | proof = Prov.prove() 66 | Verif = AggregRangeVerifier(Vs, g, h, gs, hs, u, proof) 67 | with self.subTest(seeds=seeds, vs=vs, n=n): 68 | self.assertTrue(Verif.verify()) 69 | 70 | def test_different_m(self): 71 | for _ in range(3): 72 | for i in range(1, 6): 73 | m = 2 ** i 74 | seeds = [os.urandom(10) for _ in range(7)] 75 | vs, n = [ModP(randint(0, 2 ** 16 - 1), p) for _ in range(m)], 16 76 | gs = [ 77 | elliptic_hash(str(i).encode() + seeds[0], CURVE) 78 | for i in range(n * m) 79 | ] 80 | hs = [ 81 | elliptic_hash(str(i).encode() + seeds[1], CURVE) 82 | for i in range(n * m) 83 | ] 84 | g = elliptic_hash(seeds[2], CURVE) 85 | h = elliptic_hash(seeds[3], CURVE) 86 | u = elliptic_hash(seeds[4], CURVE) 87 | gammas = [mod_hash(seeds[5], p) for _ in range(m)] 88 | Vs = [commitment(g, h, vs[i], gammas[i]) for i in range(m)] 89 | Prov = AggregNIRangeProver( 90 | vs, n, g, h, gs, hs, gammas, u, CURVE, seeds[6] 91 | ) 92 | proof = Prov.prove() 93 | Verif = AggregRangeVerifier(Vs, g, h, gs, hs, u, proof) 94 | with self.subTest(seeds=seeds, vs=vs, m=m): 95 | self.assertTrue(Verif.verify()) 96 | 97 | def test_prover_cheating_false_vs(self): 98 | m = 4 99 | seeds = [os.urandom(10) for _ in range(7)] 100 | vs, n = [ModP(randint(0, 2 ** 16 - 1), p) for _ in range(m)], 16 101 | ind = randint(0, len(vs) - 1) 102 | vs[ind] = ModP(randint(2 ** 16, 2 ** 17), p) 103 | gs = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n * m)] 104 | hs = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n * m)] 105 | g = elliptic_hash(seeds[2], CURVE) 106 | h = elliptic_hash(seeds[3], CURVE) 107 | u = elliptic_hash(seeds[4], CURVE) 108 | gammas = [mod_hash(seeds[5], p) for _ in range(m)] 109 | Vs = [commitment(g, h, vs[i], gammas[i]) for i in range(m)] 110 | Prov = AggregNIRangeProver(vs, n, g, h, gs, hs, gammas, u, CURVE, seeds[6]) 111 | proof = Prov.prove() 112 | Verif = AggregRangeVerifier(Vs, g, h, gs, hs, u, proof) 113 | with self.subTest(seeds=seeds, vs=vs, ind=ind): 114 | with self.assertRaisesRegex(Exception, "Proof invalid"): 115 | Verif.verify() 116 | 117 | def test_prover_cheating_false_commitment(self): 118 | m = 4 119 | seeds = [os.urandom(10) for _ in range(7)] 120 | vs, n = [ModP(randint(0, 2 ** 16 - 1), p) for _ in range(m)], 16 121 | gs = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n * m)] 122 | hs = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n * m)] 123 | g = elliptic_hash(seeds[2], CURVE) 124 | h = elliptic_hash(seeds[3], CURVE) 125 | u = elliptic_hash(seeds[4], CURVE) 126 | gammas = [mod_hash(seeds[5], p) for _ in range(m)] 127 | Vs = [commitment(g, h, vs[i], gammas[i]) for i in range(m)] 128 | ind = randint(0, len(Vs) - 1) 129 | Vs[ind] = commitment(g, h, vs[ind] + 1, gammas[ind]) 130 | Prov = AggregNIRangeProver(vs, n, g, h, gs, hs, gammas, u, CURVE, seeds[6]) 131 | proof = Prov.prove() 132 | Verif = AggregRangeVerifier(Vs, g, h, gs, hs, u, proof) 133 | with self.subTest(seeds=seeds, vs=vs, ind=ind): 134 | with self.assertRaisesRegex(Exception, "Proof invalid"): 135 | Verif.verify() 136 | -------------------------------------------------------------------------------- /src/tests/test_innerprod.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | from random import randint 4 | from fastecdsa.curve import secp256k1, Curve 5 | from ..innerproduct.inner_product_prover import NIProver, FastNIProver2 6 | from ..innerproduct.inner_product_verifier import Verifier1, Verifier2 7 | from ..utils.commitments import vector_commitment 8 | from ..utils.utils import mod_hash, inner_product 9 | from ..utils.elliptic_curve_hash import elliptic_hash 10 | 11 | 12 | CURVE: Curve = secp256k1 13 | 14 | 15 | class Protocol2Test(unittest.TestCase): 16 | def test_protocol_2(self): 17 | for i in range(9): 18 | seeds = [os.urandom(10) for _ in range(6)] 19 | p = CURVE.q 20 | N = 2 ** i 21 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 22 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 23 | u = elliptic_hash(seeds[2], CURVE) 24 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 25 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 26 | P = vector_commitment(g, h, a, b) + inner_product(a, b) * u 27 | Prov = FastNIProver2(g, h, u, P, a, b, CURVE) 28 | proof = Prov.prove() 29 | Verif = Verifier2(g, h, u, P, proof) 30 | with self.subTest(seeds=seeds, N=N): 31 | self.assertTrue(Verif.verify()) 32 | 33 | def test_prover_cheating_false_P_protocol2(self): 34 | seeds = [os.urandom(10) for _ in range(6)] 35 | p = CURVE.q 36 | N = 16 37 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 38 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 39 | u = elliptic_hash(seeds[2], CURVE) 40 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 41 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 42 | P = vector_commitment(g, h, a, b) + inner_product(a, b) * u 43 | Prov = FastNIProver2(g, h, u, P, a, b, CURVE) 44 | proof = Prov.prove() 45 | Verif = Verifier2(g, h, u, 2 * P, proof) 46 | with self.assertRaisesRegex(Exception, "Proof invalid"): 47 | Verif.verify() 48 | 49 | def test_prover_cheating_false_a_protocol2(self): 50 | seeds = [os.urandom(10) for _ in range(6)] 51 | p = CURVE.q 52 | N = 16 53 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 54 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 55 | u = elliptic_hash(seeds[2], CURVE) 56 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 57 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 58 | P = vector_commitment(g, h, a, b) + inner_product(a, b) * u 59 | a[randint(0, N - 1)] *= 2 60 | Prov = FastNIProver2(g, h, u, P, a, b, CURVE) 61 | proof = Prov.prove() 62 | Verif = Verifier2(g, h, u, P, proof) 63 | with self.assertRaisesRegex(Exception, "Proof invalid"): 64 | Verif.verify() 65 | 66 | def test_prover_cheating_false_b_protocol2(self): 67 | seeds = [os.urandom(10) for _ in range(6)] 68 | p = CURVE.q 69 | N = 16 70 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 71 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 72 | u = elliptic_hash(seeds[2], CURVE) 73 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 74 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 75 | P = vector_commitment(g, h, a, b) + inner_product(a, b) * u 76 | b[randint(0, N - 1)] *= 2 77 | Prov = FastNIProver2(g, h, u, P, a, b, CURVE) 78 | proof = Prov.prove() 79 | Verif = Verifier2(g, h, u, P, proof) 80 | with self.assertRaisesRegex(Exception, "Proof invalid"): 81 | Verif.verify() 82 | 83 | def test_prover_cheating_false_u_protocol2(self): 84 | seeds = [os.urandom(10) for _ in range(6)] 85 | p = CURVE.q 86 | N = 16 87 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 88 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 89 | u = elliptic_hash(seeds[2], CURVE) 90 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 91 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 92 | b[randint(0, N - 1)] *= 2 93 | P = vector_commitment(g, h, a, b) + inner_product(a, b) * u 94 | Prov = FastNIProver2(g, h, u, P, a, b, CURVE) 95 | proof = Prov.prove() 96 | Verif = Verifier2(g, h, 2 * u, P, proof) 97 | with self.assertRaisesRegex(Exception, "Proof invalid"): 98 | Verif.verify() 99 | 100 | 101 | class InnerProductArgumentTest(unittest.TestCase): 102 | def test_different_seeds(self): 103 | for _ in range(10): 104 | seeds = [os.urandom(10) for _ in range(6)] 105 | p = CURVE.q 106 | N = 16 107 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 108 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 109 | u = elliptic_hash(seeds[2], CURVE) 110 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 111 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 112 | P = vector_commitment(g, h, a, b) 113 | c = inner_product(a, b) 114 | Prov = NIProver(g, h, u, P, c, a, b, CURVE, seeds[5]) 115 | proof = Prov.prove() 116 | Verif = Verifier1(g, h, u, P, c, proof) 117 | with self.subTest(seeds=seeds): 118 | self.assertTrue(Verif.verify()) 119 | 120 | def test_different_N(self): 121 | for i in range(9): 122 | seeds = [os.urandom(10) for _ in range(6)] 123 | p = CURVE.q 124 | N = 2 ** i 125 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 126 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 127 | u = elliptic_hash(seeds[2], CURVE) 128 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 129 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 130 | P = vector_commitment(g, h, a, b) 131 | c = inner_product(a, b) 132 | Prov = NIProver(g, h, u, P, c, a, b, CURVE, seeds[5]) 133 | proof = Prov.prove() 134 | Verif = Verifier1(g, h, u, P, c, proof) 135 | with self.subTest(N=N, seeds=seeds): 136 | self.assertTrue(Verif.verify()) 137 | 138 | def test_prover_cheating_false_c(self): 139 | seeds = [os.urandom(10) for _ in range(6)] 140 | p = CURVE.q 141 | N = 16 142 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 143 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 144 | u = elliptic_hash(seeds[2], CURVE) 145 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 146 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 147 | P = vector_commitment(g, h, a, b) 148 | c = inner_product(a, b) 149 | Prov = NIProver(g, h, u, P, c + 1, a, b, CURVE, seeds[5]) 150 | proof = Prov.prove() 151 | Verif = Verifier1(g, h, u, P, c, proof) 152 | with self.assertRaisesRegex(Exception, "Proof invalid"): 153 | Verif.verify() 154 | 155 | def test_prover_cheating_false_P(self): 156 | seeds = [os.urandom(10) for _ in range(6)] 157 | p = CURVE.q 158 | N = 16 159 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 160 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 161 | u = elliptic_hash(seeds[2], CURVE) 162 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 163 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 164 | P = vector_commitment(g, h, a, b) 165 | c = inner_product(a, b) 166 | Prov = NIProver(g, h, u, P, c, a, b, CURVE, seeds[5]) 167 | proof = Prov.prove() 168 | Verif = Verifier1(g, h, u, 2 * P, c, proof) 169 | with self.assertRaisesRegex(Exception, "Proof invalid"): 170 | Verif.verify() 171 | 172 | def test_prover_cheating_false_a(self): 173 | seeds = [os.urandom(10) for _ in range(6)] 174 | p = CURVE.q 175 | N = 16 176 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 177 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 178 | u = elliptic_hash(seeds[2], CURVE) 179 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 180 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 181 | P = vector_commitment(g, h, a, b) 182 | c = inner_product(a, b) 183 | a[randint(0, N - 1)] *= 2 184 | Prov = NIProver(g, h, u, P, c, a, b, CURVE, seeds[5]) 185 | proof = Prov.prove() 186 | Verif = Verifier1(g, h, u, P, c, proof) 187 | with self.assertRaisesRegex(Exception, "Proof invalid"): 188 | Verif.verify() 189 | 190 | def test_prover_cheating_false_b(self): 191 | seeds = [os.urandom(10) for _ in range(6)] 192 | p = CURVE.q 193 | N = 16 194 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 195 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 196 | u = elliptic_hash(seeds[2], CURVE) 197 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 198 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 199 | P = vector_commitment(g, h, a, b) 200 | c = inner_product(a, b) 201 | b[randint(0, N - 1)] *= 2 202 | Prov = NIProver(g, h, u, P, c, a, b, CURVE, seeds[5]) 203 | proof = Prov.prove() 204 | Verif = Verifier1(g, h, u, P, c, proof) 205 | with self.assertRaisesRegex(Exception, "Proof invalid"): 206 | Verif.verify() 207 | 208 | def test_prover_cheating_false_u(self): 209 | seeds = [os.urandom(10) for _ in range(6)] 210 | p = CURVE.q 211 | N = 16 212 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 213 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 214 | u = elliptic_hash(seeds[2], CURVE) 215 | u *= 2 216 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 217 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 218 | P = vector_commitment(g, h, a, b) 219 | c = inner_product(a, b) 220 | Prov = NIProver(g, h, u, P, c, a, b, CURVE, seeds[5]) 221 | proof = Prov.prove() 222 | Verif = Verifier1(g, h, 2 * u, P, c, proof) 223 | with self.assertRaisesRegex(Exception, "Proof invalid"): 224 | Verif.verify() 225 | 226 | def test_prover_cheating_false_transcript1(self): 227 | seeds = [os.urandom(10) for _ in range(6)] 228 | p = CURVE.q 229 | N = 16 230 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 231 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 232 | u = elliptic_hash(seeds[2], CURVE) 233 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 234 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 235 | P = vector_commitment(g, h, a, b) 236 | c = inner_product(a, b) 237 | Prov = NIProver(g, h, u, P, c, a, b, CURVE, seeds[5]) 238 | proof = Prov.prove() 239 | randind = randint(0, len(proof.transcript) - 1) 240 | proof.transcript = ( 241 | proof.transcript[:randind] + os.urandom(1) + proof.transcript[randind + 1 :] 242 | ) 243 | Verif = Verifier1(g, h, u, P, c, proof) 244 | with self.assertRaisesRegex(Exception, "Proof invalid"): 245 | Verif.verify() 246 | 247 | def test_prover_cheating_false_transcript2(self): 248 | seeds = [os.urandom(10) for _ in range(6)] 249 | p = CURVE.q 250 | N = 16 251 | g = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(N)] 252 | h = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(N)] 253 | u = elliptic_hash(seeds[2], CURVE) 254 | a = [mod_hash(str(i).encode() + seeds[3], p) for i in range(N)] 255 | b = [mod_hash(str(i).encode() + seeds[4], p) for i in range(N)] 256 | P = vector_commitment(g, h, a, b) 257 | c = inner_product(a, b) 258 | Prov = NIProver(g, h, u, P, c, a, b, CURVE, seeds[5]) 259 | proof = Prov.prove() 260 | randind = randint(0, len(proof.proof2.transcript) - 1) 261 | proof.transcript = ( 262 | proof.proof2.transcript[:randind] 263 | + os.urandom(1) 264 | + proof.proof2.transcript[randind + 1 :] 265 | ) 266 | Verif = Verifier1(g, h, u, P, c, proof) 267 | with self.assertRaisesRegex(Exception, "Proof invalid"): 268 | Verif.verify() 269 | -------------------------------------------------------------------------------- /src/tests/test_rangeproofs.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | from random import randint 4 | from fastecdsa.curve import secp256k1 5 | from ..innerproduct.inner_product_prover import NIProver, FastNIProver2 6 | from ..innerproduct.inner_product_verifier import Verifier1, Verifier2 7 | from ..utils.commitments import vector_commitment, commitment 8 | from ..utils.utils import mod_hash, inner_product, ModP 9 | from ..utils.elliptic_curve_hash import elliptic_hash 10 | from ..rangeproofs import NIRangeProver, RangeVerifier 11 | 12 | 13 | CURVE = secp256k1 14 | p = CURVE.q 15 | 16 | 17 | class RangeProofTest(unittest.TestCase): 18 | def test_different_seeds(self): 19 | for _ in range(10): 20 | seeds = [os.urandom(10) for _ in range(7)] 21 | v, n = ModP(randint(0, 2 ** 16 - 1), p), 16 22 | gs = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n)] 23 | hs = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n)] 24 | g = elliptic_hash(seeds[2], CURVE) 25 | h = elliptic_hash(seeds[3], CURVE) 26 | u = elliptic_hash(seeds[4], CURVE) 27 | gamma = mod_hash(seeds[5], p) 28 | V = commitment(g, h, v, gamma) 29 | Prov = NIRangeProver(v, n, g, h, gs, hs, gamma, u, CURVE, seeds[6]) 30 | proof = Prov.prove() 31 | Verif = RangeVerifier(V, g, h, gs, hs, u, proof) 32 | with self.subTest(seeds=seeds): 33 | self.assertTrue(Verif.verify()) 34 | 35 | def test_different_n_and_v(self): 36 | for _ in range(3): 37 | for i in range(1, 8): 38 | seeds = [os.urandom(10) for _ in range(7)] 39 | v, n = ModP(randint(0, 2 ** (2 ** i) - 1), p), 2 ** i 40 | gs = [ 41 | elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n) 42 | ] 43 | hs = [ 44 | elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n) 45 | ] 46 | g = elliptic_hash(seeds[2], CURVE) 47 | h = elliptic_hash(seeds[3], CURVE) 48 | u = elliptic_hash(seeds[4], CURVE) 49 | gamma = mod_hash(seeds[5], p) 50 | V = commitment(g, h, v, gamma) 51 | Prov = NIRangeProver(v, n, g, h, gs, hs, gamma, u, CURVE, seeds[6]) 52 | proof = Prov.prove() 53 | Verif = RangeVerifier(V, g, h, gs, hs, u, proof) 54 | with self.subTest(v=v, n=n, seeds=seeds): 55 | self.assertTrue(Verif.verify()) 56 | 57 | def test_prover_cheating_false_v(self): 58 | seeds = [os.urandom(10) for _ in range(7)] 59 | v, n = ModP(randint(2**16,2**17), p), 16 60 | gs = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n)] 61 | hs = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n)] 62 | g = elliptic_hash(seeds[2], CURVE) 63 | h = elliptic_hash(seeds[3], CURVE) 64 | u = elliptic_hash(seeds[4], CURVE) 65 | gamma = mod_hash(seeds[5], p) 66 | V = commitment(g, h, v, gamma) 67 | Prov = NIRangeProver(v, n, g, h, gs, hs, gamma, u, CURVE, seeds[6]) 68 | proof = Prov.prove() 69 | Verif = RangeVerifier(V, g, h, gs, hs, u, proof) 70 | with self.subTest(v=v, n=n): 71 | with self.assertRaisesRegex(Exception, "Proof invalid"): 72 | Verif.verify() 73 | 74 | def test_prover_cheating_false_commitment(self): 75 | seeds = [os.urandom(10) for _ in range(7)] 76 | v, n = ModP(randint(0,2**16), p), 16 77 | gs = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n)] 78 | hs = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n)] 79 | g = elliptic_hash(seeds[2], CURVE) 80 | h = elliptic_hash(seeds[3], CURVE) 81 | u = elliptic_hash(seeds[4], CURVE) 82 | gamma = mod_hash(seeds[5], p) 83 | V = commitment(g, h, v+1, gamma) 84 | Prov = NIRangeProver(v, n, g, h, gs, hs, gamma, u, CURVE, seeds[6]) 85 | proof = Prov.prove() 86 | Verif = RangeVerifier(V, g, h, gs, hs, u, proof) 87 | with self.subTest(v=v, n=n): 88 | with self.assertRaisesRegex(Exception, "Proof invalid"): 89 | Verif.verify() 90 | 91 | def test_prover_cheating_false_transcript1(self): 92 | seeds = [os.urandom(10) for _ in range(7)] 93 | v, n = ModP(randint(0,2**16), p), 16 94 | gs = [elliptic_hash(str(i).encode() + seeds[0], CURVE) for i in range(n)] 95 | hs = [elliptic_hash(str(i).encode() + seeds[1], CURVE) for i in range(n)] 96 | g = elliptic_hash(seeds[2], CURVE) 97 | h = elliptic_hash(seeds[3], CURVE) 98 | u = elliptic_hash(seeds[4], CURVE) 99 | gamma = mod_hash(seeds[5], p) 100 | V = commitment(g, h, v+1, gamma) 101 | Prov = NIRangeProver(v, n, g, h, gs, hs, gamma, u, CURVE, seeds[6]) 102 | proof = Prov.prove() 103 | while True: 104 | randind = randint(0, len(proof.transcript) - 1) 105 | new = str(randint(0,9)).encode() 106 | if proof.transcript[randind] not in [b'&', new]: 107 | proof.transcript = ( 108 | proof.transcript[:randind] + new + proof.transcript[randind + 1 :] 109 | ) 110 | break 111 | Verif = RangeVerifier(V, g, h, gs, hs, u, proof) 112 | with self.subTest(v=v, n=n, randind=randind): 113 | with self.assertRaisesRegex(Exception, "Proof invalid"): 114 | Verif.verify() 115 | -------------------------------------------------------------------------------- /src/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | from random import randint 4 | from fastecdsa.curve import secp256k1 5 | 6 | from ..utils.utils import ( 7 | mod_hash, 8 | bytes_to_point, 9 | point_to_bytes, 10 | b64_to_point, 11 | point_to_b64, 12 | ) 13 | from ..utils.elliptic_curve_hash import elliptic_hash 14 | 15 | CURVE = secp256k1 16 | 17 | 18 | class HashTest(unittest.TestCase): 19 | def test_mod_hash(self): 20 | p = 1009 21 | x = mod_hash(b"test", p) 22 | self.assertLess(x.x, p) 23 | self.assertEqual(x, mod_hash(b"test", p)) 24 | p = 17 25 | for _ in range(100): 26 | msg = os.urandom(10) 27 | x = mod_hash(msg, p) 28 | with self.subTest(msg=msg, p=p): 29 | self.assertNotEqual(x.x, 0) 30 | 31 | def test_elliptic_hash(self): 32 | for _ in range(100): 33 | msg = os.urandom(10) 34 | x = elliptic_hash(msg, CURVE) 35 | with self.subTest(msg=msg): 36 | self.assertTrue(CURVE.is_point_on_curve((x.x, x.y))) 37 | 38 | 39 | class ConversionTest(unittest.TestCase): 40 | def test_point_to_bytes(self): 41 | for _ in range(100): 42 | e = randint(0, CURVE.q) 43 | x = e * CURVE.G 44 | with self.subTest(e=e): 45 | self.assertEqual(bytes_to_point(point_to_bytes(x)), x) 46 | 47 | def test_point_to_b64(self): 48 | for _ in range(100): 49 | e = randint(0, CURVE.q) 50 | x = e * CURVE.G 51 | with self.subTest(e=e): 52 | self.assertEqual(b64_to_point(point_to_b64(x)), x) 53 | 54 | -------------------------------------------------------------------------------- /src/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wborgeaud/python-bulletproofs/967e1a54928b0130624fba6eeb3a19b42362b83d/src/utils/__init__.py -------------------------------------------------------------------------------- /src/utils/commitments.py: -------------------------------------------------------------------------------- 1 | from fastecdsa.point import Point 2 | from ..pippenger import PipSECP256k1 3 | 4 | 5 | def commitment(g, h, x, r): 6 | return x * g + r * h 7 | 8 | 9 | def vector_commitment(g, h, a, b): 10 | assert len(g) == len(h) == len(a) == len(b) 11 | # return sum([ai*gi for ai,gi in zip(a,g)], Point(None,None,None)) \ 12 | # + sum([bi*hi for bi,hi in zip(b,h)], Point(None,None,None)) 13 | return PipSECP256k1.multiexp(g + h, a + b) 14 | 15 | 16 | def _mult(a: int, g: Point) -> Point: 17 | if a < 0 and abs(a) < 2 ** 32: 18 | return abs(a) * _inv(g) 19 | else: 20 | return a * g 21 | 22 | 23 | def _inv(g: Point) -> Point: 24 | return Point(g.x, -g.y, g.curve) 25 | -------------------------------------------------------------------------------- /src/utils/elliptic_curve_hash.py: -------------------------------------------------------------------------------- 1 | from fastecdsa.point import Point 2 | from fastecdsa.curve import Curve 3 | from fastecdsa.util import mod_sqrt 4 | from hashlib import sha256, md5 5 | 6 | 7 | def elliptic_hash(msg: bytes, CURVE: Curve): 8 | p = CURVE.p 9 | i = 0 10 | while True: 11 | i += 1 12 | prefixed_msg = str(i).encode() + msg 13 | h = sha256(prefixed_msg).hexdigest() 14 | x = int(h, 16) 15 | if x >= p: 16 | continue 17 | 18 | y_sq = (x ** 3 + CURVE.a * x + CURVE.b) % p 19 | y = mod_sqrt(y_sq, p)[0] 20 | 21 | if CURVE.is_point_on_curve((x, y)): 22 | b = int(md5(prefixed_msg).hexdigest(), 16) % 2 23 | return Point(x, y, CURVE) if b else Point(x, p - y, CURVE) 24 | 25 | -------------------------------------------------------------------------------- /src/utils/transcript.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | from .utils import mod_hash, point_to_b64 4 | 5 | 6 | class Transcript: 7 | """ 8 | Transcript class. 9 | Contains all parameters used to generate randomness using Fiat-Shamir 10 | Separate every entity by a '&'. 11 | """ 12 | 13 | def __init__(self, seed=b""): 14 | self.digest = base64.b64encode(seed) + b"&" 15 | 16 | def add_point(self, g): 17 | """Add an elliptic curve point to the transcript""" 18 | self.digest += point_to_b64(g) 19 | self.digest += b"&" 20 | 21 | def add_list_points(self, gs): 22 | """Add a list of elliptic curve point to the transcript""" 23 | for g in gs: 24 | self.add_point(g) 25 | 26 | def add_number(self, x): 27 | """Add a number to the transcript""" 28 | self.digest += str(x).encode() 29 | self.digest += b"&" 30 | 31 | def get_modp(self, p): 32 | """Generate a number as the hash of the digest""" 33 | return mod_hash(self.digest, p) 34 | -------------------------------------------------------------------------------- /src/utils/utils.py: -------------------------------------------------------------------------------- 1 | """Contains various utilities""" 2 | 3 | from hashlib import sha256 4 | from typing import List 5 | import base64 6 | 7 | from fastecdsa.point import Point 8 | from fastecdsa.curve import secp256k1 9 | from fastecdsa.util import mod_sqrt 10 | 11 | CURVE = secp256k1 12 | BYTE_LENGTH = CURVE.q.bit_length() // 8 13 | 14 | 15 | def egcd(a, b): 16 | """Extended euclid algorithm""" 17 | if a == 0: 18 | return (b, 0, 1) 19 | else: 20 | g, y, x = egcd(b % a, a) 21 | return (g, x - (b // a) * y, y) 22 | 23 | 24 | class ModP: 25 | """Class representing an integer mod p""" 26 | 27 | def __init__(self, x, p): 28 | self.x = x 29 | self.p = p 30 | 31 | def __add__(self, y): 32 | if isinstance(y, int): 33 | return ModP(self.x + y, self.p) 34 | assert self.p == y.p 35 | return ModP((self.x + y.x) % self.p, self.p) 36 | 37 | def __radd__(self, y): 38 | return self + y 39 | 40 | def __mul__(self, y): 41 | if isinstance(y, int): 42 | return ModP(self.x * y, self.p) 43 | if isinstance(y, Point): 44 | return self.x * y 45 | assert self.p == y.p 46 | return ModP((self.x * y.x) % self.p, self.p) 47 | 48 | def __sub__(self, y): 49 | if isinstance(y, int): 50 | return ModP((self.x - y) % self.p, self.p) 51 | assert self.p == y.p 52 | return ModP((self.x - y.x) % self.p, self.p) 53 | 54 | def __rsub__(self, y): 55 | return -(self - y) 56 | 57 | def __pow__(self, n): 58 | return ModP(pow(self.x, n, self.p), self.p) 59 | 60 | def __mod__(self, other): 61 | return self.x % other 62 | 63 | def __neg__(self): 64 | return ModP(self.p - self.x, self.p) 65 | 66 | def inv(self): 67 | """Returns the modular inverse""" 68 | g, a, _ = egcd(self.x, self.p) 69 | if g != 1: 70 | raise Exception("modular inverse does not exist") 71 | else: 72 | return ModP(a % self.p, self.p) 73 | 74 | def __eq__(self, y): 75 | return (self.p == y.p) and (self.x % self.p == y.x % self.p) 76 | 77 | def __str__(self): 78 | return str(self.x) 79 | 80 | def __repr__(self): 81 | return str(self.x) 82 | 83 | 84 | def mod_hash(msg: bytes, p: int, non_zero: bool = True) -> ModP: 85 | """Takes a message and a prime and returns a hash in ModP""" 86 | i = 0 87 | while True: 88 | i += 1 89 | prefixed_msg = str(i).encode() + msg 90 | h = sha256(prefixed_msg).hexdigest() 91 | x = int(h, 16) % 2 ** p.bit_length() 92 | if x >= p: 93 | continue 94 | elif non_zero and x == 0: 95 | continue 96 | else: 97 | return ModP(x, p) 98 | 99 | 100 | def point_to_bytes(g: Point) -> bytes: 101 | """Takes an EC point and returns the compressed bytes representation""" 102 | if g == Point.IDENTITY_ELEMENT: 103 | return b"\x00" 104 | x_enc = g.x.to_bytes(BYTE_LENGTH, "big") 105 | prefix = b"\x03" if g.y % 2 else b"\x02" 106 | return prefix + x_enc 107 | 108 | 109 | def point_to_b64(g: Point) -> bytes: 110 | """Takes an EC point and returns the base64 compressed bytes representation""" 111 | return base64.b64encode(point_to_bytes(g)) 112 | 113 | 114 | def b64_to_point(s: bytes) -> Point: 115 | """Takes a base64 compressed bytes representation and returns the corresponding point""" 116 | return bytes_to_point(base64.b64decode(s)) 117 | 118 | 119 | def bytes_to_point(b: bytes) -> Point: 120 | """Takes a compressed bytes representation and returns the corresponding point""" 121 | if b == 0: 122 | return Point.IDENTITY_ELEMENT 123 | p = CURVE.p 124 | yp, x_enc = b[0], b[1:] 125 | yp = 0 if yp == 2 else 1 126 | x = int.from_bytes(x_enc, "big") 127 | y = mod_sqrt((x ** 3 + CURVE.a * x + CURVE.b) % p, p)[0] 128 | if y % 2 == yp: 129 | return Point(x, y, CURVE) 130 | else: 131 | return Point(x, p - y, CURVE) 132 | 133 | 134 | def inner_product(a: List[ModP], b: List[ModP]) -> ModP: 135 | """Inner-product of vectors in Z_p""" 136 | assert len(a) == len(b) 137 | return sum([ai * bi for ai, bi in zip(a, b)], ModP(0, a[0].p)) 138 | --------------------------------------------------------------------------------