Merge branch 'master' of https://github.com/ethereum/remix-project into git4
commit
5c0a0a610f
@ -0,0 +1,42 @@ |
|||||||
|
#!/bin/bash |
||||||
|
|
||||||
|
# Determine the OS platform |
||||||
|
OS="$(uname)" |
||||||
|
|
||||||
|
if [ "$OS" == "Darwin" ]; then |
||||||
|
# macOS systems |
||||||
|
if [ -e "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ]; then |
||||||
|
version=$("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --version) |
||||||
|
echo "Google Chrome version on macOS: $version" |
||||||
|
else |
||||||
|
echo "Google Chrome is not installed on your macOS." |
||||||
|
fi |
||||||
|
elif [ "$OS" == "Linux" ]; then |
||||||
|
# Linux systems |
||||||
|
if command -v google-chrome >/dev/null; then |
||||||
|
version=$(google-chrome --version) |
||||||
|
echo "Google Chrome version on Linux: $version" |
||||||
|
else |
||||||
|
echo "Google Chrome is not installed on your Linux." |
||||||
|
fi |
||||||
|
else |
||||||
|
echo "Unsupported OS." |
||||||
|
fi |
||||||
|
|
||||||
|
MAJORVERSION=$(echo "$version" | grep -Eo '[0-9]+\.' | head -1 | cut -d'.' -f1) |
||||||
|
echo "CHROME DRIVER INSTALL $MAJORVERSION" |
||||||
|
|
||||||
|
# Specify the directory to check |
||||||
|
directory="./tmp/webdrivers" |
||||||
|
|
||||||
|
# Check if the directory exists |
||||||
|
if [ -d "$directory" ]; then |
||||||
|
echo "Directory exists: $directory" |
||||||
|
else |
||||||
|
echo "Directory does not exist. Creating directory: $directory" |
||||||
|
mkdir -p "$directory" |
||||||
|
fi |
||||||
|
|
||||||
|
|
||||||
|
yarn init -y --cwd "$directory" || exit 1 |
||||||
|
yarn add -D chromedriver@$MAJORVERSION geckodriver --cwd "$directory" || yarn add -D chromedriver@$MAJORVERSION geckodriver --cwd "$directory" || yarn add -D chromedriver geckodriver --cwd "$directory" || exit 1 |
@ -0,0 +1,54 @@ |
|||||||
|
module.exports = { |
||||||
|
src_folders: ['dist/apps/remix-ide-e2e/src/tests'], |
||||||
|
output_folder: './reports/tests', |
||||||
|
custom_commands_path: ['dist/apps/remix-ide-e2e/src/commands'], |
||||||
|
custom_assertions_path: '', |
||||||
|
page_objects_path: '', |
||||||
|
globals_path: '', |
||||||
|
|
||||||
|
webdriver: { |
||||||
|
start_process: true, |
||||||
|
port: 4444, |
||||||
|
server_path: './tmp/webdrivers/node_modules/geckodriver/bin/geckodriver.js', |
||||||
|
}, |
||||||
|
|
||||||
|
test_settings: { |
||||||
|
selenium_port: 4444, |
||||||
|
selenium_host: 'localhost', |
||||||
|
'default': { |
||||||
|
globals: { |
||||||
|
waitForConditionTimeout: 10000, |
||||||
|
asyncHookTimeout: 100000 |
||||||
|
}, |
||||||
|
screenshots: { |
||||||
|
enabled: true, |
||||||
|
path: './reports/screenshots', |
||||||
|
on_failure: true, |
||||||
|
on_error: true |
||||||
|
}, |
||||||
|
exclude: ['dist/apps/remix-ide-e2e/src/tests/runAndDeploy.test.js', 'dist/apps/remix-ide-e2e/src/tests/pluginManager.test.ts'] |
||||||
|
}, |
||||||
|
|
||||||
|
'firefoxDesktop': { |
||||||
|
desiredCapabilities: { |
||||||
|
'browserName': 'firefox', |
||||||
|
'javascriptEnabled': true, |
||||||
|
'acceptSslCerts': true, |
||||||
|
'moz:firefoxOptions': { |
||||||
|
args: ['-width=2560', '-height=1440'] |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
'firefox': { |
||||||
|
desiredCapabilities: { |
||||||
|
'browserName': 'firefox', |
||||||
|
'javascriptEnabled': true, |
||||||
|
'acceptSslCerts': true, |
||||||
|
'moz:firefoxOptions': { |
||||||
|
args: ['-headless', '-width=2560', '-height=1440'] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,106 @@ |
|||||||
|
'use strict' |
||||||
|
import { NightwatchBrowser } from 'nightwatch' |
||||||
|
import init from '../helpers/init' |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
'@disabled': true, |
||||||
|
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||||
|
init(browser, done) |
||||||
|
}, |
||||||
|
'Should show text in pinned contracts section #group1': function (browser: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.assert.elementPresent('*[data-id="pinnedContracts"]') |
||||||
|
.assert.textContains('*[data-id="pinnedContractsSublabel"]', '(network: vm-cancun)') |
||||||
|
.assert.elementPresent('*[data-id="NoPinnedInstanceText"]') |
||||||
|
.assert.textContains('*[data-id="NoPinnedInstanceText"]', 'No pinned contracts found for selected workspace & network') |
||||||
|
}, |
||||||
|
'Deploy & pin contract #group1': function (browser: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.clickLaunchIcon('filePanel') |
||||||
|
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||||
|
.click('*[data-id="treeViewLitreeViewItemcontracts"]') |
||||||
|
.click('*[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]') |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.click('*[data-id="Deploy - transact (not payable)"]') |
||||||
|
.assert.elementPresent('*[data-id="unpinnedInstance0xd9145CCE52D386f254917e481eB44e9943F39138"]') |
||||||
|
.click('*[data-id="universalDappUiUdappPin"]') |
||||||
|
.assert.elementPresent('*[data-id="deployAndRunNoInstanceText"]') |
||||||
|
.assert.textContains('*[data-id="deployAndRunNoInstanceText"]', 'Currently you have no unpinned contracts to interact with.') |
||||||
|
.assert.not.elementPresent('*[data-id="NoPinnedInstanceText"]') |
||||||
|
.assert.elementPresent('*[data-id="pinnedInstance0xd9145CCE52D386f254917e481eB44e9943F39138"]') |
||||||
|
}, |
||||||
|
'Test pinned contract loading on environment change #group1': function (browser: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.switchEnvironment('vm-shanghai') |
||||||
|
.assert.elementPresent('*[data-id="pinnedContracts"]') |
||||||
|
.assert.textContains('*[data-id="pinnedContractsSublabel"]', '(network: vm-shanghai)') |
||||||
|
.assert.elementPresent('*[data-id="NoPinnedInstanceText"]') |
||||||
|
.assert.textContains('*[data-id="NoPinnedInstanceText"]', 'No pinned contracts found for selected workspace & network') |
||||||
|
.switchEnvironment('vm-cancun') |
||||||
|
.assert.textContains('*[data-id="pinnedContractsSublabel"]', '(network: vm-cancun)') |
||||||
|
.assert.not.elementPresent('*[data-id="NoPinnedInstanceText"]') |
||||||
|
.assert.elementPresent('*[data-id="pinnedInstance0xd9145CCE52D386f254917e481eB44e9943F39138"]') |
||||||
|
}, |
||||||
|
'Interact with pinned contract #group1': function (browser: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.click('*[data-id="universalDappUiTitleExpander0"]') |
||||||
|
.assert.elementPresent('*[data-id="instanceContractBal"]') |
||||||
|
.assert.elementPresent('*[data-id="instanceContractPinnedAt"]') |
||||||
|
.assert.elementPresent('*[data-id="instanceContractFilePath"]') |
||||||
|
.assert.textContains('*[data-id="instanceContractFilePath"]', 'default_workspace/contracts/1_Storage.sol') |
||||||
|
.clickFunction('retrieve - call') |
||||||
|
.testFunction('last', |
||||||
|
{ |
||||||
|
to: 'Storage.retrieve() 0xd9145CCE52D386f254917e481eB44e9943F39138', |
||||||
|
'decoded output': { "0": "uint256: 0" } |
||||||
|
}) |
||||||
|
.clickFunction('store - transact (not payable)', { types: 'uint256 num', values: '35' }) |
||||||
|
.testFunction('last', |
||||||
|
{ |
||||||
|
status: '0x1 Transaction mined and execution succeed', |
||||||
|
'decoded input': { "uint256 num": "35" } |
||||||
|
}) |
||||||
|
.clickFunction('retrieve - call') |
||||||
|
.testFunction('last', |
||||||
|
{ |
||||||
|
to: 'Storage.retrieve() 0xd9145CCE52D386f254917e481eB44e9943F39138', |
||||||
|
'decoded output': { "0": "uint256: 35" } |
||||||
|
}) |
||||||
|
}, |
||||||
|
'Unpin & interact #group1': function (browser: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.click('*[data-id="universalDappUiUdappUnpin"]') |
||||||
|
.assert.textContains('*[data-id="NoPinnedInstanceText"]', 'No pinned contracts found for selected workspace & network') |
||||||
|
.assert.not.elementPresent('*[data-id="deployAndRunNoInstanceText"]') |
||||||
|
.click('*[data-id="universalDappUiTitleExpander0"]') |
||||||
|
.assert.not.elementPresent('*[data-id="instanceContractPinnedAt"]') |
||||||
|
.assert.not.elementPresent('*[data-id="instanceContractFilePath"]') |
||||||
|
.clickFunction('retrieve - call') |
||||||
|
.testFunction('last', |
||||||
|
{ |
||||||
|
to: 'Storage.retrieve() 0xd9145CCE52D386f254917e481eB44e9943F39138', |
||||||
|
'decoded output': { "0": "uint256: 35" } |
||||||
|
}) |
||||||
|
.clickFunction('store - transact (not payable)', { types: 'uint256 num', values: '55' }) |
||||||
|
.testFunction('last', |
||||||
|
{ |
||||||
|
status: '0x1 Transaction mined and execution succeed', |
||||||
|
'decoded input': { "uint256 num": "55" } |
||||||
|
}) |
||||||
|
.clickFunction('retrieve - call') |
||||||
|
.testFunction('last', |
||||||
|
{ |
||||||
|
to: 'Storage.retrieve() 0xd9145CCE52D386f254917e481eB44e9943F39138', |
||||||
|
'decoded output': { "0": "uint256: 55" } |
||||||
|
}) |
||||||
|
}, |
||||||
|
'Re-pin & delete immediately #group1': function (browser: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.click('*[data-id="universalDappUiUdappPin"]') |
||||||
|
.assert.elementPresent('*[data-id="deployAndRunNoInstanceText"]') |
||||||
|
.click('*[data-id="universalDappUiUdappDelete"]') |
||||||
|
.assert.textContains('*[data-id="NoPinnedInstanceText"]', 'No pinned contracts found for selected workspace & network') |
||||||
|
.assert.textContains('*[data-id="deployAndRunNoInstanceText"]', 'Currently you have no unpinned contracts to interact with.') |
||||||
|
}, |
||||||
|
} |
@ -0,0 +1,272 @@ |
|||||||
|
'use strict' |
||||||
|
import { NightwatchBrowser } from 'nightwatch' |
||||||
|
import init from '../helpers/init' |
||||||
|
|
||||||
|
const passphrase = process.env.account_passphrase |
||||||
|
const password = process.env.account_password |
||||||
|
const extension_id = 'nkbihfbeogaeaoehlefnkodbefgpgknn' |
||||||
|
const extension_url = `chrome-extension://${extension_id}/home.html` |
||||||
|
|
||||||
|
const checkBrowserIsChrome = function (browser: NightwatchBrowser) { |
||||||
|
return browser.browserName.indexOf('chrome') > -1 |
||||||
|
} |
||||||
|
|
||||||
|
const checkAlerts = function (browser: NightwatchBrowser){ |
||||||
|
browser.isVisible({ |
||||||
|
selector: '//*[contains(.,"not have enough")]', |
||||||
|
locateStrategy: 'xpath', |
||||||
|
suppressNotFoundErrors: true, |
||||||
|
timeout: 3000 |
||||||
|
}, (okVisible) => { |
||||||
|
if (okVisible.value) { |
||||||
|
browser.assert.fail('Not enough ETH in test account!!') |
||||||
|
browser.end() |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
'@disabled': true, |
||||||
|
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||||
|
init(browser, done) |
||||||
|
}, |
||||||
|
|
||||||
|
'@sources': function () { |
||||||
|
return sources |
||||||
|
}, |
||||||
|
|
||||||
|
'Should connect to Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') |
||||||
|
.setupMetamask(passphrase, password)
|
||||||
|
.useCss().switchBrowserTab(0) |
||||||
|
.refreshPage() |
||||||
|
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||||
|
.click('*[data-id="landingPageStartSolidity"]') |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.switchEnvironment('MetaMask') |
||||||
|
.waitForElementPresent('*[data-id="settingsNetworkEnv"]') |
||||||
|
.assert.containsText('*[data-id="settingsNetworkEnv"]', 'Sepolia (11155111) network') |
||||||
|
.pause(5000) |
||||||
|
.switchBrowserWindow(extension_url, 'MetaMask', (browser) => { |
||||||
|
browser |
||||||
|
.waitForElementVisible('*[data-testid="page-container-footer-next"]') |
||||||
|
.click('*[data-testid="page-container-footer-next"]') // this connects the metamask account to remix
|
||||||
|
.pause(2000) |
||||||
|
.waitForElementVisible('*[data-testid="page-container-footer-next"]') |
||||||
|
.click('*[data-testid="page-container-footer-next"]') |
||||||
|
// .waitForElementVisible('*[data-testid="popover-close"]')
|
||||||
|
// .click('*[data-testid="popover-close"]')
|
||||||
|
}) |
||||||
|
.switchBrowserTab(0) // back to remix
|
||||||
|
}, |
||||||
|
|
||||||
|
'Should add a contract file #group1': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') |
||||||
|
.clickLaunchIcon('filePanel') |
||||||
|
.addFile('Greet.sol', sources[0]['Greet.sol']) |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.waitForElementVisible('*[data-id="Deploy - transact (not payable)"]', 45000) // wait for the contract to compile
|
||||||
|
}, |
||||||
|
|
||||||
|
'Should deploy contract on Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
browser.clearConsole().waitForElementPresent('*[data-id="runTabSelectAccount"] option', 45000) |
||||||
|
.clickLaunchIcon('filePanel') |
||||||
|
.openFile('Greet.sol') |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') |
||||||
|
.click('*[data-id="Deploy - transact (not payable)"]') |
||||||
|
.pause(5000) |
||||||
|
.perform((done) => { |
||||||
|
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => { |
||||||
|
checkAlerts(browser) |
||||||
|
browser |
||||||
|
.waitForElementPresent('[data-testid="page-container-footer-next"]') |
||||||
|
.click('[data-testid="page-container-footer-next"]') // approve the tx
|
||||||
|
.switchBrowserTab(0) // back to remix
|
||||||
|
.waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000) |
||||||
|
.waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000) |
||||||
|
.perform(() => done()) |
||||||
|
}) |
||||||
|
})
|
||||||
|
}, |
||||||
|
|
||||||
|
'Should run low level interaction (fallback function) on Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
browser.clearConsole().waitForElementPresent('*[data-id="remixIdeSidePanel"]') |
||||||
|
.clickInstance(0) |
||||||
|
.waitForElementPresent('*[data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"]') |
||||||
|
.click('*[data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"]') |
||||||
|
.perform((done) => { |
||||||
|
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => { |
||||||
|
browser |
||||||
|
.waitForElementPresent('[data-testid="page-container-footer-next"]') |
||||||
|
.click('[data-testid="page-container-footer-next"]') // approve the tx
|
||||||
|
.switchBrowserTab(0) // back to remix
|
||||||
|
.waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000) |
||||||
|
.waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000) |
||||||
|
.perform(() => done()) |
||||||
|
})
|
||||||
|
})
|
||||||
|
}, |
||||||
|
|
||||||
|
'Should connect to Ethereum Main Network using MetaMask #group1': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') |
||||||
|
.switchBrowserTab(1) |
||||||
|
.click('[data-testid="network-display"]') |
||||||
|
.click('div[data-testid="Ethereum Mainnet"]') // switch to mainnet
|
||||||
|
.useCss().switchBrowserTab(0) |
||||||
|
.refreshPage() |
||||||
|
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||||
|
.click('*[data-id="landingPageStartSolidity"]') |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.switchEnvironment('MetaMask') |
||||||
|
.waitForElementPresent('*[data-id="settingsNetworkEnv"]') |
||||||
|
.assert.containsText('*[data-id="settingsNetworkEnv"]', 'Main (1) network') |
||||||
|
}, |
||||||
|
|
||||||
|
'Should deploy contract on Ethereum Main Network using MetaMask #group1': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
browser.waitForElementPresent('*[data-id="runTabSelectAccount"] option') |
||||||
|
.clickLaunchIcon('filePanel') |
||||||
|
.openFile('Greet.sol') |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') |
||||||
|
.click('*[data-id="Deploy - transact (not payable)"]') |
||||||
|
.waitForElementVisible('*[data-id="udappNotifyModalDialogModalBody-react"]', 65000) |
||||||
|
.modalFooterOKClick('udappNotify') |
||||||
|
.pause(10000) |
||||||
|
.assert.containsText('*[data-id="udappNotifyModalDialogModalBody-react"]', 'You are about to create a transaction on Main Network. Confirm the details to send the info to your provider.') |
||||||
|
.modalFooterCancelClick('udappNotify') |
||||||
|
}, |
||||||
|
|
||||||
|
'Should deploy Ballot to Sepolia using metamask': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') |
||||||
|
.switchBrowserTab(1) |
||||||
|
.click('[data-testid="network-display"]') |
||||||
|
.click('div[data-testid="Sepolia"]') // switch to sepolia
|
||||||
|
.useCss().switchBrowserTab(0) |
||||||
|
.openFile('contracts') |
||||||
|
.openFile('contracts/3_Ballot.sol') |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.clearConsole() |
||||||
|
.clearTransactions() |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.waitForElementVisible('input[placeholder="bytes32[] proposalNames"]') |
||||||
|
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]') |
||||||
|
.click('*[data-id="Deploy - transact (not payable)"]') // deploy ballot
|
||||||
|
.perform((done) => { |
||||||
|
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => { |
||||||
|
browser |
||||||
|
.waitForElementPresent('[data-testid="page-container-footer-next"]') |
||||||
|
.click('[data-testid="page-container-footer-next"]') // approve the tx
|
||||||
|
.switchBrowserTab(0) // back to remix
|
||||||
|
.waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000) |
||||||
|
.waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000) |
||||||
|
.perform(() => done()) |
||||||
|
}) |
||||||
|
})
|
||||||
|
.waitForElementPresent('*[data-id="universalDappUiContractActionWrapper"]', 60000) |
||||||
|
.clearConsole() |
||||||
|
.clickInstance(0) |
||||||
|
.clickFunction('delegate - transact (not payable)', { types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"' }) |
||||||
|
.perform((done) => { // call delegate
|
||||||
|
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => { |
||||||
|
browser |
||||||
|
.waitForElementPresent('[data-testid="page-container-footer-next"]') |
||||||
|
.click('[data-testid="page-container-footer-next"]') // approve the tx
|
||||||
|
.switchBrowserTab(0) // back to remix
|
||||||
|
.waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000) |
||||||
|
.waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000) |
||||||
|
.perform(() => done()) |
||||||
|
}) |
||||||
|
}) |
||||||
|
.testFunction('last', |
||||||
|
{ |
||||||
|
status: '0x1 Transaction mined and execution succeed', |
||||||
|
'decoded input': { 'address to': '0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB' } |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
/* |
||||||
|
* This test is using 2 different services: |
||||||
|
* - Metamask for getting the transaction |
||||||
|
* - Sepolia node for retrieving the trace and storage |
||||||
|
*/ |
||||||
|
'Should debug Sepolia transaction with source highlighting MetaMask #group1': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
let txhash
|
||||||
|
browser.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||||
|
.clickLaunchIcon('pluginManager') // load debugger and source verification
|
||||||
|
// .scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_sourcify"] button')
|
||||||
|
// debugger already activated .scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_debugger"] button')
|
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.perform((done) => { |
||||||
|
browser.getLastTransactionHash((hash) => { |
||||||
|
txhash = hash |
||||||
|
done() |
||||||
|
}) |
||||||
|
}) |
||||||
|
.perform((done) => { |
||||||
|
browser |
||||||
|
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) |
||||||
|
.clickLaunchIcon('debugger') |
||||||
|
.setValue('*[data-id="debuggerTransactionInput"]', txhash) // debug tx
|
||||||
|
.click('*[data-id="debuggerTransactionStartButton"]') |
||||||
|
.waitForElementVisible('*[data-id="treeViewDivto"]', 30000) |
||||||
|
.checkVariableDebug('soliditylocals', localsCheck) |
||||||
|
.perform(() => done()) |
||||||
|
}) |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
'Call web3.eth.getAccounts() using Injected Provider (Metamask) #group1': function (browser: NightwatchBrowser) { |
||||||
|
if (!checkBrowserIsChrome(browser)) return |
||||||
|
browser |
||||||
|
.executeScriptInTerminal('web3.eth.getAccounts()') |
||||||
|
.journalLastChildIncludes('["0x76a3ABb5a12dcd603B52Ed22195dED17ee82708f"]') |
||||||
|
}
|
||||||
|
} |
||||||
|
|
||||||
|
const localsCheck = { |
||||||
|
to: { |
||||||
|
value: '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB', |
||||||
|
type: 'address' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const sources = [ |
||||||
|
{ |
||||||
|
'Greet.sol': { |
||||||
|
content: |
||||||
|
` |
||||||
|
pragma solidity ^0.8.0; |
||||||
|
contract HelloWorld { |
||||||
|
string public message; |
||||||
|
|
||||||
|
fallback () external { |
||||||
|
message = 'Hello World!'; |
||||||
|
} |
||||||
|
|
||||||
|
function greet(string memory _message) public { |
||||||
|
message = _message; |
||||||
|
} |
||||||
|
}` |
||||||
|
}, |
||||||
|
'checkBalance.sol': { |
||||||
|
content: `pragma solidity ^0.8.0;
|
||||||
|
contract CheckBalance { |
||||||
|
constructor () payable {} |
||||||
|
|
||||||
|
function sendSomeEther(uint256 num) public { |
||||||
|
payable(msg.sender).transfer(num); |
||||||
|
} |
||||||
|
|
||||||
|
}` |
||||||
|
} |
||||||
|
} |
||||||
|
] |
@ -0,0 +1,32 @@ |
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('circuit-compiler', 'generateR1cs', 'circuits/calculate_hash.circom'); |
||||||
|
|
||||||
|
const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; |
||||||
|
// @ts-ignore
|
||||||
|
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.r1cs', { encoding: null }); |
||||||
|
// @ts-ignore
|
||||||
|
const r1cs = new Uint8Array(r1csBuffer); |
||||||
|
const zkey_final = { type: "mem" }; |
||||||
|
|
||||||
|
console.log('plonk setup') |
||||||
|
await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final) |
||||||
|
|
||||||
|
console.log('exportVerificationKey') |
||||||
|
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) |
||||||
|
|
||||||
|
console.log('save zkey_final') |
||||||
|
await remix.call('fileManager', 'writeFile', './zk/keys/plonk/zkey_final.txt', JSON.stringify(Array.from(((zkey_final as any).data)))) |
||||||
|
|
||||||
|
console.log('save verification key') |
||||||
|
await remix.call('fileManager', 'writeFile', './zk/keys/plonk/verification_key.json', JSON.stringify(vKey, null, 2)) |
||||||
|
|
||||||
|
console.log('setup done') |
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
@ -0,0 +1,93 @@ |
|||||||
|
import { ethers, BigNumber } from 'ethers' |
||||||
|
import { poseidon } from "circomlibjs" // v0.0.8
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
const logger = { |
||||||
|
info: (...args) => console.log(...args), |
||||||
|
debug: (...args) => console.log(...args), |
||||||
|
error: (...args) => console.error(...args), |
||||||
|
}; |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('circuit-compiler', 'compile', 'circuits/calculate_hash.circom'); |
||||||
|
// @ts-ignore
|
||||||
|
const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/calculate_hash.wasm', { encoding: null }); |
||||||
|
// @ts-ignore
|
||||||
|
const wasm = new Uint8Array(wasmBuffer); |
||||||
|
const zkey_final = { |
||||||
|
type: "mem", |
||||||
|
data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/zkey_final.txt'))) |
||||||
|
} |
||||||
|
|
||||||
|
const wtns = { type: "mem" }; |
||||||
|
const value1 = '1234' |
||||||
|
const value2 = '2' |
||||||
|
const value3 = '3' |
||||||
|
const value4 = '4' |
||||||
|
|
||||||
|
const wrongValue = '5' // put this in the poseidon hash calculation to simulate a non matching hash.
|
||||||
|
|
||||||
|
const signals = { |
||||||
|
value1, |
||||||
|
value2, |
||||||
|
value3, |
||||||
|
value4, |
||||||
|
hash: poseidon([value1, value2, value3, value4]) |
||||||
|
} |
||||||
|
|
||||||
|
console.log('calculate') |
||||||
|
await snarkjs.wtns.calculate(signals, wasm, wtns, logger); |
||||||
|
|
||||||
|
const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns); |
||||||
|
|
||||||
|
const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) |
||||||
|
|
||||||
|
const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof); |
||||||
|
|
||||||
|
console.log('zk proof validity', verified); |
||||||
|
const templates = { |
||||||
|
plonk: await remix.call('fileManager', 'readFile', 'templates/plonk_verifier.sol.ejs') |
||||||
|
} |
||||||
|
const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) |
||||||
|
|
||||||
|
await remix.call('fileManager', 'writeFile', 'zk/build/plonk/zk_verifier.sol', solidityContract) |
||||||
|
await remix.call('fileManager', 'writeFile', 'zk/build/plonk/input.json', JSON.stringify({ |
||||||
|
_proof: [ |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.B[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.B[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.C[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.C[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Z[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Z[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T1[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T1[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T2[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T2[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T3[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T3[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxi[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxi[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxiw[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxiw[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_a).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_b).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_c).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_s1).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_s2).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_zw).toHexString(), 32), |
||||||
|
], |
||||||
|
_pubSignals: publicSignals |
||||||
|
}, null, 2)) |
||||||
|
|
||||||
|
console.log('proof done.') |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
@ -0,0 +1,710 @@ |
|||||||
|
// SPDX-License-Identifier: GPL-3.0 |
||||||
|
/* |
||||||
|
Copyright 2021 0KIMS association. |
||||||
|
|
||||||
|
This file is generated with [snarkJS](https://github.com/iden3/snarkjs). |
||||||
|
|
||||||
|
snarkJS is a free software: you can redistribute it and/or modify it |
||||||
|
under the terms of the GNU General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
snarkJS is distributed in the hope that it will be useful, but WITHOUT |
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
||||||
|
License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License |
||||||
|
along with snarkJS. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
pragma solidity >=0.7.0 <0.9.0; |
||||||
|
|
||||||
|
import "hardhat/console.sol"; |
||||||
|
|
||||||
|
contract PlonkVerifier { |
||||||
|
// Omega |
||||||
|
uint256 constant w1 = <%=w%>; |
||||||
|
// Scalar field size |
||||||
|
uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; |
||||||
|
// Base field size |
||||||
|
uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583; |
||||||
|
|
||||||
|
// [1]_1 |
||||||
|
uint256 constant G1x = 1; |
||||||
|
uint256 constant G1y = 2; |
||||||
|
// [1]_2 |
||||||
|
uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; |
||||||
|
uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; |
||||||
|
uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; |
||||||
|
uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; |
||||||
|
|
||||||
|
// Verification Key data |
||||||
|
uint32 constant n = <%=2**power%>; |
||||||
|
uint16 constant nPublic = <%=nPublic%>; |
||||||
|
uint16 constant nLagrange = <%=Math.max(nPublic, 1)%>; |
||||||
|
|
||||||
|
uint256 constant Qmx = <%=Qm[0]%>; |
||||||
|
uint256 constant Qmy = <%=Qm[0] == "0" ? "0" : Qm[1]%>; |
||||||
|
uint256 constant Qlx = <%=Ql[0]%>; |
||||||
|
uint256 constant Qly = <%=Ql[0] == "0" ? "0" : Ql[1]%>; |
||||||
|
uint256 constant Qrx = <%=Qr[0]%>; |
||||||
|
uint256 constant Qry = <%=Qr[0] == "0" ? "0" : Qr[1]%>; |
||||||
|
uint256 constant Qox = <%=Qo[0]%>; |
||||||
|
uint256 constant Qoy = <%=Qo[0] == "0" ? "0" : Qo[1]%>; |
||||||
|
uint256 constant Qcx = <%=Qc[0]%>; |
||||||
|
uint256 constant Qcy = <%=Qc[0] == "0" ? "0" : Qc[1]%>; |
||||||
|
uint256 constant S1x = <%=S1[0]%>; |
||||||
|
uint256 constant S1y = <%=S1[0] == "0" ? "0" : S1[1]%>; |
||||||
|
uint256 constant S2x = <%=S2[0]%>; |
||||||
|
uint256 constant S2y = <%=S2[0] == "0" ? "0" : S2[1]%>; |
||||||
|
uint256 constant S3x = <%=S3[0]%>; |
||||||
|
uint256 constant S3y = <%=S3[0] == "0" ? "0" : S3[1]%>; |
||||||
|
uint256 constant k1 = <%=k1%>; |
||||||
|
uint256 constant k2 = <%=k2%>; |
||||||
|
uint256 constant X2x1 = <%=X_2[0][0]%>; |
||||||
|
uint256 constant X2x2 = <%=X_2[0][1]%>; |
||||||
|
uint256 constant X2y1 = <%=X_2[1][0]%>; |
||||||
|
uint256 constant X2y2 = <%=X_2[1][1]%>; |
||||||
|
|
||||||
|
// Proof calldata |
||||||
|
// Byte offset of every parameter of the calldata |
||||||
|
// Polynomial commitments |
||||||
|
uint16 constant pA = 4 + 0; |
||||||
|
uint16 constant pB = 4 + 64; |
||||||
|
uint16 constant pC = 4 + 128; |
||||||
|
uint16 constant pZ = 4 + 192; |
||||||
|
uint16 constant pT1 = 4 + 256; |
||||||
|
uint16 constant pT2 = 4 + 320; |
||||||
|
uint16 constant pT3 = 4 + 384; |
||||||
|
uint16 constant pWxi = 4 + 448; |
||||||
|
uint16 constant pWxiw = 4 + 512; |
||||||
|
// Opening evaluations |
||||||
|
uint16 constant pEval_a = 4 + 576; |
||||||
|
uint16 constant pEval_b = 4 + 608; |
||||||
|
uint16 constant pEval_c = 4 + 640; |
||||||
|
uint16 constant pEval_s1 = 4 + 672; |
||||||
|
uint16 constant pEval_s2 = 4 + 704; |
||||||
|
uint16 constant pEval_zw = 4 + 736; |
||||||
|
|
||||||
|
// Memory data |
||||||
|
// Challenges |
||||||
|
uint16 constant pAlpha = 0; |
||||||
|
uint16 constant pBeta = 32; |
||||||
|
uint16 constant pGamma = 64; |
||||||
|
uint16 constant pXi = 96; |
||||||
|
uint16 constant pXin = 128; |
||||||
|
uint16 constant pBetaXi = 160; |
||||||
|
uint16 constant pV1 = 192; |
||||||
|
uint16 constant pV2 = 224; |
||||||
|
uint16 constant pV3 = 256; |
||||||
|
uint16 constant pV4 = 288; |
||||||
|
uint16 constant pV5 = 320; |
||||||
|
uint16 constant pU = 352; |
||||||
|
|
||||||
|
uint16 constant pPI = 384; |
||||||
|
uint16 constant pEval_r0 = 416; |
||||||
|
uint16 constant pD = 448; |
||||||
|
uint16 constant pF = 512; |
||||||
|
uint16 constant pE = 576; |
||||||
|
uint16 constant pTmp = 640; |
||||||
|
uint16 constant pAlpha2 = 704; |
||||||
|
uint16 constant pZh = 736; |
||||||
|
uint16 constant pZhInv = 768; |
||||||
|
|
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
uint16 constant pEval_l<%=i%> = <%=768+i*32%>; |
||||||
|
<% } %> |
||||||
|
<% let pLastMem = 800+32*Math.max(nPublic,1) %> |
||||||
|
|
||||||
|
uint16 constant lastMem = <%=pLastMem%>; |
||||||
|
|
||||||
|
function verifyProof(uint256[24] calldata _proof, uint256[<%=nPublic%>] calldata _pubSignals) public view returns (bool) { |
||||||
|
assembly { |
||||||
|
///////// |
||||||
|
// Computes the inverse using the extended euclidean algorithm |
||||||
|
///////// |
||||||
|
function inverse(a, q) -> inv { |
||||||
|
let t := 0 |
||||||
|
let newt := 1 |
||||||
|
let r := q |
||||||
|
let newr := a |
||||||
|
let quotient |
||||||
|
let aux |
||||||
|
|
||||||
|
for { } newr { } { |
||||||
|
quotient := sdiv(r, newr) |
||||||
|
aux := sub(t, mul(quotient, newt)) |
||||||
|
t:= newt |
||||||
|
newt:= aux |
||||||
|
|
||||||
|
aux := sub(r,mul(quotient, newr)) |
||||||
|
r := newr |
||||||
|
newr := aux |
||||||
|
} |
||||||
|
|
||||||
|
if gt(r, 1) { revert(0,0) } |
||||||
|
if slt(t, 0) { t:= add(t, q) } |
||||||
|
|
||||||
|
inv := t |
||||||
|
} |
||||||
|
|
||||||
|
/////// |
||||||
|
// Computes the inverse of an array of values |
||||||
|
// See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations |
||||||
|
////// |
||||||
|
function inverseArray(pVals, n) { |
||||||
|
|
||||||
|
let pAux := mload(0x40) // Point to the next free position |
||||||
|
let pIn := pVals |
||||||
|
let lastPIn := add(pVals, mul(n, 32)) // Read n elemnts |
||||||
|
let acc := mload(pIn) // Read the first element |
||||||
|
pIn := add(pIn, 32) // Point to the second element |
||||||
|
let inv |
||||||
|
|
||||||
|
|
||||||
|
for { } lt(pIn, lastPIn) { |
||||||
|
pAux := add(pAux, 32) |
||||||
|
pIn := add(pIn, 32) |
||||||
|
} |
||||||
|
{ |
||||||
|
mstore(pAux, acc) |
||||||
|
acc := mulmod(acc, mload(pIn), q) |
||||||
|
} |
||||||
|
acc := inverse(acc, q) |
||||||
|
|
||||||
|
// At this point pAux pint to the next free position we substract 1 to point to the last used |
||||||
|
pAux := sub(pAux, 32) |
||||||
|
// pIn points to the n+1 element, we substract to point to n |
||||||
|
pIn := sub(pIn, 32) |
||||||
|
lastPIn := pVals // We don't process the first element |
||||||
|
for { } gt(pIn, lastPIn) { |
||||||
|
pAux := sub(pAux, 32) |
||||||
|
pIn := sub(pIn, 32) |
||||||
|
} |
||||||
|
{ |
||||||
|
inv := mulmod(acc, mload(pAux), q) |
||||||
|
acc := mulmod(acc, mload(pIn), q) |
||||||
|
mstore(pIn, inv) |
||||||
|
} |
||||||
|
// pIn points to first element, we just set it. |
||||||
|
mstore(pIn, acc) |
||||||
|
} |
||||||
|
|
||||||
|
function checkField(v) { |
||||||
|
if iszero(lt(v, q)) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function checkInput() { |
||||||
|
checkField(calldataload(pEval_a)) |
||||||
|
checkField(calldataload(pEval_b)) |
||||||
|
checkField(calldataload(pEval_c)) |
||||||
|
checkField(calldataload(pEval_s1)) |
||||||
|
checkField(calldataload(pEval_s2)) |
||||||
|
checkField(calldataload(pEval_zw)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateChallenges(pMem, pPublic) { |
||||||
|
let beta |
||||||
|
let aux |
||||||
|
|
||||||
|
let mIn := mload(0x40) // Pointer to the next free memory position |
||||||
|
|
||||||
|
// Compute challenge.beta & challenge.gamma |
||||||
|
mstore(mIn, Qmx) |
||||||
|
mstore(add(mIn, 32), Qmy) |
||||||
|
mstore(add(mIn, 64), Qlx) |
||||||
|
mstore(add(mIn, 96), Qly) |
||||||
|
mstore(add(mIn, 128), Qrx) |
||||||
|
mstore(add(mIn, 160), Qry) |
||||||
|
mstore(add(mIn, 192), Qox) |
||||||
|
mstore(add(mIn, 224), Qoy) |
||||||
|
mstore(add(mIn, 256), Qcx) |
||||||
|
mstore(add(mIn, 288), Qcy) |
||||||
|
mstore(add(mIn, 320), S1x) |
||||||
|
mstore(add(mIn, 352), S1y) |
||||||
|
mstore(add(mIn, 384), S2x) |
||||||
|
mstore(add(mIn, 416), S2y) |
||||||
|
mstore(add(mIn, 448), S3x) |
||||||
|
mstore(add(mIn, 480), S3y) |
||||||
|
|
||||||
|
<%for (let i=0; i<nPublic;i++) {%> |
||||||
|
mstore(add(mIn, <%= 512 + i*32 %>), calldataload(add(pPublic, <%=i*32%>))) |
||||||
|
<%}%> |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 0 %> ), calldataload(pA)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 32 %> ), calldataload(add(pA, 32))) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 64 %> ), calldataload(pB)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 96 %> ), calldataload(add(pB, 32))) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 128 %> ), calldataload(pC)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 160 %> ), calldataload(add(pC, 32))) |
||||||
|
|
||||||
|
beta := mod(keccak256(mIn, <%= 704 + 32 * nPublic %>), q) |
||||||
|
mstore(add(pMem, pBeta), beta) |
||||||
|
|
||||||
|
// challenges.gamma |
||||||
|
mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q)) |
||||||
|
|
||||||
|
// challenges.alpha |
||||||
|
mstore(mIn, mload(add(pMem, pBeta))) |
||||||
|
mstore(add(mIn, 32), mload(add(pMem, pGamma))) |
||||||
|
mstore(add(mIn, 64), calldataload(pZ)) |
||||||
|
mstore(add(mIn, 96), calldataload(add(pZ, 32))) |
||||||
|
|
||||||
|
aux := mod(keccak256(mIn, 128), q) |
||||||
|
mstore(add(pMem, pAlpha), aux) |
||||||
|
mstore(add(pMem, pAlpha2), mulmod(aux, aux, q)) |
||||||
|
|
||||||
|
// challenges.xi |
||||||
|
mstore(mIn, aux) |
||||||
|
mstore(add(mIn, 32), calldataload(pT1)) |
||||||
|
mstore(add(mIn, 64), calldataload(add(pT1, 32))) |
||||||
|
mstore(add(mIn, 96), calldataload(pT2)) |
||||||
|
mstore(add(mIn, 128), calldataload(add(pT2, 32))) |
||||||
|
mstore(add(mIn, 160), calldataload(pT3)) |
||||||
|
mstore(add(mIn, 192), calldataload(add(pT3, 32))) |
||||||
|
|
||||||
|
aux := mod(keccak256(mIn, 224), q) |
||||||
|
mstore( add(pMem, pXi), aux) |
||||||
|
|
||||||
|
// challenges.v |
||||||
|
mstore(mIn, aux) |
||||||
|
mstore(add(mIn, 32), calldataload(pEval_a)) |
||||||
|
mstore(add(mIn, 64), calldataload(pEval_b)) |
||||||
|
mstore(add(mIn, 96), calldataload(pEval_c)) |
||||||
|
mstore(add(mIn, 128), calldataload(pEval_s1)) |
||||||
|
mstore(add(mIn, 160), calldataload(pEval_s2)) |
||||||
|
mstore(add(mIn, 192), calldataload(pEval_zw)) |
||||||
|
|
||||||
|
let v1 := mod(keccak256(mIn, 224), q) |
||||||
|
mstore(add(pMem, pV1), v1) |
||||||
|
|
||||||
|
// challenges.beta * challenges.xi |
||||||
|
mstore(add(pMem, pBetaXi), mulmod(beta, aux, q)) |
||||||
|
|
||||||
|
// challenges.xi^n |
||||||
|
<%for (let i=0; i<power;i++) {%> |
||||||
|
aux:= mulmod(aux, aux, q) |
||||||
|
<%}%> |
||||||
|
mstore(add(pMem, pXin), aux) |
||||||
|
|
||||||
|
// Zh |
||||||
|
aux:= mod(add(sub(aux, 1), q), q) |
||||||
|
mstore(add(pMem, pZh), aux) |
||||||
|
mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols |
||||||
|
|
||||||
|
// challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5 |
||||||
|
aux := mulmod(v1, v1, q) |
||||||
|
mstore(add(pMem, pV2), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV3), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV4), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV5), aux) |
||||||
|
|
||||||
|
// challenges.u |
||||||
|
mstore(mIn, calldataload(pWxi)) |
||||||
|
mstore(add(mIn, 32), calldataload(add(pWxi, 32))) |
||||||
|
mstore(add(mIn, 64), calldataload(pWxiw)) |
||||||
|
mstore(add(mIn, 96), calldataload(add(pWxiw, 32))) |
||||||
|
|
||||||
|
mstore(add(pMem, pU), mod(keccak256(mIn, 128), q)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateLagrange(pMem) { |
||||||
|
let w := 1 |
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l<%=i%>), |
||||||
|
mulmod( |
||||||
|
n, |
||||||
|
mod( |
||||||
|
add( |
||||||
|
sub( |
||||||
|
mload(add(pMem, pXi)), |
||||||
|
w |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% if (i<Math.max(nPublic, 1)) { %> |
||||||
|
w := mulmod(w, w1, q) |
||||||
|
<% } %> |
||||||
|
<% } %> |
||||||
|
|
||||||
|
inverseArray(add(pMem, pZhInv), <%=Math.max(nPublic, 1)+1%> ) |
||||||
|
|
||||||
|
let zh := mload(add(pMem, pZh)) |
||||||
|
w := 1 |
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
<% if (i==1) { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l1 ), |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l1 )), |
||||||
|
zh, |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% } else { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l<%=i%>), |
||||||
|
mulmod( |
||||||
|
w, |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l<%=i%>)), |
||||||
|
zh, |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% } %> |
||||||
|
<% if (i<Math.max(nPublic, 1)) { %> |
||||||
|
w := mulmod(w, w1, q) |
||||||
|
<% } %> |
||||||
|
<% } %> |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function calculatePI(pMem, pPub) { |
||||||
|
let pl := 0 |
||||||
|
|
||||||
|
<% for (let i=0; i<nPublic; i++) { %> |
||||||
|
pl := mod( |
||||||
|
add( |
||||||
|
sub( |
||||||
|
pl, |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l<%=i+1%>)), |
||||||
|
calldataload(add(pPub, <%=i*32%>)), |
||||||
|
q |
||||||
|
) |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
<% } %> |
||||||
|
|
||||||
|
mstore(add(pMem, pPI), pl) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateR0(pMem) { |
||||||
|
let e1 := mload(add(pMem, pPI)) |
||||||
|
|
||||||
|
let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q) |
||||||
|
|
||||||
|
let e3a := addmod( |
||||||
|
calldataload(pEval_a), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), |
||||||
|
q) |
||||||
|
e3a := addmod(e3a, mload(add(pMem, pGamma)), q) |
||||||
|
|
||||||
|
let e3b := addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), |
||||||
|
q) |
||||||
|
e3b := addmod(e3b, mload(add(pMem, pGamma)), q) |
||||||
|
|
||||||
|
let e3c := addmod( |
||||||
|
calldataload(pEval_c), |
||||||
|
mload(add(pMem, pGamma)), |
||||||
|
q) |
||||||
|
|
||||||
|
let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q) |
||||||
|
e3 := mulmod(e3, calldataload(pEval_zw), q) |
||||||
|
e3 := mulmod(e3, mload(add(pMem, pAlpha)), q) |
||||||
|
|
||||||
|
let r0 := addmod(e1, mod(sub(q, e2), q), q) |
||||||
|
r0 := addmod(r0, mod(sub(q, e3), q), q) |
||||||
|
|
||||||
|
mstore(add(pMem, pEval_r0) , r0) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_set(pR, pP) { |
||||||
|
mstore(pR, mload(pP)) |
||||||
|
mstore(add(pR, 32), mload(add(pP,32))) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_setC(pR, x, y) { |
||||||
|
mstore(pR, x) |
||||||
|
mstore(add(pR, 32), y) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_calldataSet(pR, pP) { |
||||||
|
mstore(pR, calldataload(pP)) |
||||||
|
mstore(add(pR, 32), calldataload(add(pP, 32))) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_acc(pR, pP) { |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, mload(pR)) |
||||||
|
mstore(add(mIn,32), mload(add(pR, 32))) |
||||||
|
mstore(add(mIn,64), mload(pP)) |
||||||
|
mstore(add(mIn,96), mload(add(pP, 32))) |
||||||
|
|
||||||
|
let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulAcc(pR, pP, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, mload(pP)) |
||||||
|
mstore(add(mIn,32), mload(add(pP, 32))) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
mstore(add(mIn,64), mload(pR)) |
||||||
|
mstore(add(mIn,96), mload(add(pR, 32))) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulAccC(pR, x, y, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, x) |
||||||
|
mstore(add(mIn,32), y) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
mstore(add(mIn,64), mload(pR)) |
||||||
|
mstore(add(mIn,96), mload(add(pR, 32))) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulSetC(pR, x, y, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, x) |
||||||
|
mstore(add(mIn,32), y) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulSet(pR, pP, s) { |
||||||
|
g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateD(pMem) { |
||||||
|
let _pD:= add(pMem, pD) |
||||||
|
let gamma := mload(add(pMem, pGamma)) |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes) |
||||||
|
|
||||||
|
g1_setC(_pD, Qcx, Qcy) |
||||||
|
g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q)) |
||||||
|
g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a)) |
||||||
|
g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b)) |
||||||
|
g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c)) |
||||||
|
|
||||||
|
let betaxi := mload(add(pMem, pBetaXi)) |
||||||
|
let val1 := addmod( |
||||||
|
addmod(calldataload(pEval_a), betaxi, q), |
||||||
|
gamma, q) |
||||||
|
|
||||||
|
let val2 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(betaxi, k1, q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
let val3 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_c), |
||||||
|
mulmod(betaxi, k2, q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
let d2a := mulmod( |
||||||
|
mulmod(mulmod(val1, val2, q), val3, q), |
||||||
|
mload(add(pMem, pAlpha)), |
||||||
|
q |
||||||
|
) |
||||||
|
|
||||||
|
let d2b := mulmod( |
||||||
|
mload(add(pMem, pEval_l1)), |
||||||
|
mload(add(pMem, pAlpha2)), |
||||||
|
q |
||||||
|
) |
||||||
|
|
||||||
|
// We'll use mIn to save d2 |
||||||
|
g1_calldataSet(add(mIn, 192), pZ) |
||||||
|
g1_mulSet( |
||||||
|
mIn, |
||||||
|
add(mIn, 192), |
||||||
|
addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q)) |
||||||
|
|
||||||
|
|
||||||
|
val1 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_a), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
val2 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
val3 := mulmod( |
||||||
|
mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q), |
||||||
|
calldataload(pEval_zw), q) |
||||||
|
|
||||||
|
|
||||||
|
// We'll use mIn + 64 to save d3 |
||||||
|
g1_mulSetC( |
||||||
|
add(mIn, 64), |
||||||
|
S3x, |
||||||
|
S3y, |
||||||
|
mulmod(mulmod(val1, val2, q), val3, q)) |
||||||
|
|
||||||
|
// We'll use mIn + 128 to save d4 |
||||||
|
g1_calldataSet(add(mIn, 128), pT1) |
||||||
|
|
||||||
|
g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin))) |
||||||
|
let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q) |
||||||
|
g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2) |
||||||
|
|
||||||
|
g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh))) |
||||||
|
|
||||||
|
mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf)) |
||||||
|
mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf)) |
||||||
|
g1_acc(_pD, mIn) |
||||||
|
g1_acc(_pD, add(mIn, 64)) |
||||||
|
g1_acc(_pD, add(mIn, 128)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateF(pMem) { |
||||||
|
let p := add(pMem, pF) |
||||||
|
|
||||||
|
g1_set(p, add(pMem, pD)) |
||||||
|
g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1))) |
||||||
|
g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2))) |
||||||
|
g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3))) |
||||||
|
g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4))) |
||||||
|
g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5))) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateE(pMem) { |
||||||
|
let s := mod(sub(q, mload(add(pMem, pEval_r0))), q) |
||||||
|
|
||||||
|
s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q) |
||||||
|
|
||||||
|
g1_mulSetC(add(pMem, pE), G1x, G1y, s) |
||||||
|
} |
||||||
|
|
||||||
|
function checkPairing(pMem) -> isOk { |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw |
||||||
|
|
||||||
|
let _pWxi := add(mIn, 384) |
||||||
|
let _pWxiw := add(mIn, 448) |
||||||
|
let _aux := add(mIn, 512) |
||||||
|
|
||||||
|
g1_calldataSet(_pWxi, pWxi) |
||||||
|
g1_calldataSet(_pWxiw, pWxiw) |
||||||
|
|
||||||
|
// A1 |
||||||
|
g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU))) |
||||||
|
g1_acc(mIn, _pWxi) |
||||||
|
mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf)) |
||||||
|
|
||||||
|
// [X]_2 |
||||||
|
mstore(add(mIn,64), X2x2) |
||||||
|
mstore(add(mIn,96), X2x1) |
||||||
|
mstore(add(mIn,128), X2y2) |
||||||
|
mstore(add(mIn,160), X2y1) |
||||||
|
|
||||||
|
// B1 |
||||||
|
g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi))) |
||||||
|
|
||||||
|
let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q) |
||||||
|
s := mulmod(s, w1, q) |
||||||
|
g1_mulSet(_aux, _pWxiw, s) |
||||||
|
g1_acc(add(mIn, 192), _aux) |
||||||
|
g1_acc(add(mIn, 192), add(pMem, pF)) |
||||||
|
mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf)) |
||||||
|
g1_acc(add(mIn, 192), add(pMem, pE)) |
||||||
|
|
||||||
|
// [1]_2 |
||||||
|
mstore(add(mIn,256), G2x2) |
||||||
|
mstore(add(mIn,288), G2x1) |
||||||
|
mstore(add(mIn,320), G2y2) |
||||||
|
mstore(add(mIn,352), G2y1) |
||||||
|
|
||||||
|
let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20) |
||||||
|
|
||||||
|
isOk := and(success, mload(mIn)) |
||||||
|
} |
||||||
|
|
||||||
|
let pMem := mload(0x40) |
||||||
|
mstore(0x40, add(pMem, lastMem)) |
||||||
|
|
||||||
|
checkInput() |
||||||
|
calculateChallenges(pMem, _pubSignals) |
||||||
|
calculateLagrange(pMem) |
||||||
|
calculatePI(pMem, _pubSignals) |
||||||
|
calculateR0(pMem) |
||||||
|
calculateD(pMem) |
||||||
|
calculateF(pMem) |
||||||
|
calculateE(pMem) |
||||||
|
let isValid := checkPairing(pMem) |
||||||
|
|
||||||
|
mstore(0x40, sub(pMem, lastMem)) |
||||||
|
mstore(0, isValid) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('circuit-compiler', 'generateR1cs', 'circuits/rln.circom'); |
||||||
|
|
||||||
|
const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmciCq5JcZQyTLvC9GRanrLBi82ZmSriq1Fr5zANkGHebf"; |
||||||
|
// @ts-ignore
|
||||||
|
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', { encoding: null }); |
||||||
|
// @ts-ignore
|
||||||
|
const r1cs = new Uint8Array(r1csBuffer); |
||||||
|
const zkey_final = { type: "mem" }; |
||||||
|
|
||||||
|
console.log('plonk setup') |
||||||
|
await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final) |
||||||
|
|
||||||
|
console.log('exportVerificationKey') |
||||||
|
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) |
||||||
|
|
||||||
|
console.log('save zkey_final') |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('fileManager', 'writeFile', './zk/keys/plonk/zkey_final.txt', (zkey_final as any).data, { encoding: null }) |
||||||
|
|
||||||
|
console.log('save verification key') |
||||||
|
await remix.call('fileManager', 'writeFile', './zk/keys/plonk/verification_key.json', JSON.stringify(vKey, null, 2)) |
||||||
|
|
||||||
|
console.log('setup done') |
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
@ -0,0 +1,183 @@ |
|||||||
|
import { ethers, BigNumber } from 'ethers' |
||||||
|
import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree" |
||||||
|
import { poseidon } from "circomlibjs" // v0.0.8
|
||||||
|
|
||||||
|
import { ZqField } from 'ffjavascript' |
||||||
|
const SNARK_FIELD_SIZE = BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617') |
||||||
|
|
||||||
|
// Creates the finite field
|
||||||
|
const Fq = new ZqField(SNARK_FIELD_SIZE) |
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
const logger = { |
||||||
|
info: (...args) => console.log(...args), |
||||||
|
debug: (...args) => console.log(...args), |
||||||
|
error: (...args) => console.error(...args), |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Recovers secret from two shares |
||||||
|
* @param x1 signal hash of first message |
||||||
|
* @param x2 signal hash of second message |
||||||
|
* @param y1 yshare of first message |
||||||
|
* @param y2 yshare of second message |
||||||
|
* @returns identity secret |
||||||
|
*/ |
||||||
|
function shamirRecovery(x1: bigint, x2: bigint, y1: bigint, y2: bigint): bigint { |
||||||
|
const slope = Fq.div(Fq.sub(y2, y1), Fq.sub(x2, x1)) |
||||||
|
const privateKey = Fq.sub(y1, Fq.mul(slope, x1)) |
||||||
|
|
||||||
|
return Fq.normalize(privateKey) |
||||||
|
} |
||||||
|
|
||||||
|
function hash(message: any): bigint { |
||||||
|
message = BigNumber.from(message).toTwos(256).toHexString() |
||||||
|
message = ethers.utils.zeroPad(message, 32) |
||||||
|
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) |
||||||
|
} |
||||||
|
|
||||||
|
function hashNullifier(message: any): bigint { |
||||||
|
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) |
||||||
|
} |
||||||
|
|
||||||
|
async function prove (signals, wasm, wtns, r1cs, zkey_final, vKey) { |
||||||
|
console.log('calculate') |
||||||
|
await snarkjs.wtns.calculate(signals, wasm, wtns); |
||||||
|
|
||||||
|
console.log('check') |
||||||
|
await snarkjs.wtns.check(r1cs, wtns, logger);
|
||||||
|
|
||||||
|
console.log('prove') |
||||||
|
const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns); |
||||||
|
|
||||||
|
const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof, logger); |
||||||
|
console.log('zk proof validity', verified); |
||||||
|
|
||||||
|
await remix.call('fileManager', 'writeFile', `zk/build/plonk/input-${Date.now()}.json`, JSON.stringify({ |
||||||
|
_pubSignals: publicSignals, |
||||||
|
_proof: [ |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.B[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.B[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.C[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.C[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Z[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Z[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T1[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T1[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T2[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T2[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T3[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T3[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxi[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxi[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxiw[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxiw[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_a).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_b).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_c).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_s1).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_s2).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_zw).toHexString(), 32), |
||||||
|
] |
||||||
|
}, null, 2)) |
||||||
|
|
||||||
|
console.log('proof done.') |
||||||
|
return { |
||||||
|
proof, |
||||||
|
x: publicSignals[3], |
||||||
|
y: publicSignals[0] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.r1cs', { encoding: null }); |
||||||
|
// @ts-ignore
|
||||||
|
const r1cs = new Uint8Array(r1csBuffer); |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('circuit-compiler', 'compile', 'circuits/rln.circom'); |
||||||
|
// @ts-ignore
|
||||||
|
const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/rln.wasm', { encoding: null }); |
||||||
|
// @ts-ignore
|
||||||
|
const wasm = new Uint8Array(wasmBuffer);
|
||||||
|
|
||||||
|
const zkey_final = { |
||||||
|
type: "mem", |
||||||
|
// @ts-ignore
|
||||||
|
data: new Uint8Array(await remix.call('fileManager', 'readFile', './zk/keys/plonk/zkey_final.txt', { encoding: null })) |
||||||
|
} |
||||||
|
const wtns = { type: "mem" }; |
||||||
|
|
||||||
|
const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) |
||||||
|
|
||||||
|
// build list of identity commitments
|
||||||
|
const secrets = [] |
||||||
|
const identityCommitments = [] |
||||||
|
const rateCommitments = [] |
||||||
|
const userMessageLimit = 0x2 |
||||||
|
for (let k = 0; k < 2; k++) {
|
||||||
|
const identitySecret = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) |
||||||
|
secrets.push(identitySecret) |
||||||
|
|
||||||
|
const identityCommitment = poseidon([identitySecret]) |
||||||
|
const rateCommitment = poseidon([identityCommitment, userMessageLimit]) |
||||||
|
identityCommitments.push(identityCommitment) |
||||||
|
rateCommitments.push(rateCommitment) |
||||||
|
} |
||||||
|
|
||||||
|
let tree |
||||||
|
|
||||||
|
try { |
||||||
|
tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, rateCommitments) // Binary tree.
|
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const merkleproof1 = tree.createProof(0) |
||||||
|
|
||||||
|
const appId = 0xa |
||||||
|
const epoch = 0x1 |
||||||
|
|
||||||
|
const signals1 = { |
||||||
|
identitySecret: secrets[0], |
||||||
|
userMessageLimit, |
||||||
|
messageId: 0x0, |
||||||
|
pathElements: merkleproof1.siblings, |
||||||
|
identityPathIndex: merkleproof1.pathIndices, |
||||||
|
x: 0xabcd, // hash(message)
|
||||||
|
externalNullifier: 0xa // hash(epoch, appId)
|
||||||
|
} |
||||||
|
const proof1 = await prove(signals1, wasm, wtns, r1cs, zkey_final, vKey) |
||||||
|
|
||||||
|
const signals2 = { |
||||||
|
identitySecret: secrets[0], |
||||||
|
userMessageLimit, |
||||||
|
messageId: 0x0, |
||||||
|
pathElements: merkleproof1.siblings, |
||||||
|
identityPathIndex: merkleproof1.pathIndices, |
||||||
|
x: 0xabce, // hash(message)
|
||||||
|
externalNullifier: 0xa // hash(epoch, appId)
|
||||||
|
} |
||||||
|
const proof2 = await prove(signals2, wasm, wtns, r1cs, zkey_final, vKey) |
||||||
|
|
||||||
|
const secret = shamirRecovery(BigInt(proof1.x), BigInt(proof2.x), BigInt(proof1.y), BigInt(proof2.y)) |
||||||
|
|
||||||
|
console.log(secret.toString(10)) |
||||||
|
console.log(Fq.normalize(secrets[0])) |
||||||
|
|
||||||
|
const templates = { |
||||||
|
plonk: await remix.call('fileManager', 'readFile', 'templates/plonk_verifier.sol.ejs') |
||||||
|
} |
||||||
|
const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) |
||||||
|
|
||||||
|
await remix.call('fileManager', 'writeFile', './zk/build/plonk/zk_verifier.sol', solidityContract) |
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
@ -0,0 +1,710 @@ |
|||||||
|
// SPDX-License-Identifier: GPL-3.0 |
||||||
|
/* |
||||||
|
Copyright 2021 0KIMS association. |
||||||
|
|
||||||
|
This file is generated with [snarkJS](https://github.com/iden3/snarkjs). |
||||||
|
|
||||||
|
snarkJS is a free software: you can redistribute it and/or modify it |
||||||
|
under the terms of the GNU General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
snarkJS is distributed in the hope that it will be useful, but WITHOUT |
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
||||||
|
License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License |
||||||
|
along with snarkJS. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
pragma solidity >=0.7.0 <0.9.0; |
||||||
|
|
||||||
|
import "hardhat/console.sol"; |
||||||
|
|
||||||
|
contract PlonkVerifier { |
||||||
|
// Omega |
||||||
|
uint256 constant w1 = <%=w%>; |
||||||
|
// Scalar field size |
||||||
|
uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; |
||||||
|
// Base field size |
||||||
|
uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583; |
||||||
|
|
||||||
|
// [1]_1 |
||||||
|
uint256 constant G1x = 1; |
||||||
|
uint256 constant G1y = 2; |
||||||
|
// [1]_2 |
||||||
|
uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; |
||||||
|
uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; |
||||||
|
uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; |
||||||
|
uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; |
||||||
|
|
||||||
|
// Verification Key data |
||||||
|
uint32 constant n = <%=2**power%>; |
||||||
|
uint16 constant nPublic = <%=nPublic%>; |
||||||
|
uint16 constant nLagrange = <%=Math.max(nPublic, 1)%>; |
||||||
|
|
||||||
|
uint256 constant Qmx = <%=Qm[0]%>; |
||||||
|
uint256 constant Qmy = <%=Qm[0] == "0" ? "0" : Qm[1]%>; |
||||||
|
uint256 constant Qlx = <%=Ql[0]%>; |
||||||
|
uint256 constant Qly = <%=Ql[0] == "0" ? "0" : Ql[1]%>; |
||||||
|
uint256 constant Qrx = <%=Qr[0]%>; |
||||||
|
uint256 constant Qry = <%=Qr[0] == "0" ? "0" : Qr[1]%>; |
||||||
|
uint256 constant Qox = <%=Qo[0]%>; |
||||||
|
uint256 constant Qoy = <%=Qo[0] == "0" ? "0" : Qo[1]%>; |
||||||
|
uint256 constant Qcx = <%=Qc[0]%>; |
||||||
|
uint256 constant Qcy = <%=Qc[0] == "0" ? "0" : Qc[1]%>; |
||||||
|
uint256 constant S1x = <%=S1[0]%>; |
||||||
|
uint256 constant S1y = <%=S1[0] == "0" ? "0" : S1[1]%>; |
||||||
|
uint256 constant S2x = <%=S2[0]%>; |
||||||
|
uint256 constant S2y = <%=S2[0] == "0" ? "0" : S2[1]%>; |
||||||
|
uint256 constant S3x = <%=S3[0]%>; |
||||||
|
uint256 constant S3y = <%=S3[0] == "0" ? "0" : S3[1]%>; |
||||||
|
uint256 constant k1 = <%=k1%>; |
||||||
|
uint256 constant k2 = <%=k2%>; |
||||||
|
uint256 constant X2x1 = <%=X_2[0][0]%>; |
||||||
|
uint256 constant X2x2 = <%=X_2[0][1]%>; |
||||||
|
uint256 constant X2y1 = <%=X_2[1][0]%>; |
||||||
|
uint256 constant X2y2 = <%=X_2[1][1]%>; |
||||||
|
|
||||||
|
// Proof calldata |
||||||
|
// Byte offset of every parameter of the calldata |
||||||
|
// Polynomial commitments |
||||||
|
uint16 constant pA = 4 + 0; |
||||||
|
uint16 constant pB = 4 + 64; |
||||||
|
uint16 constant pC = 4 + 128; |
||||||
|
uint16 constant pZ = 4 + 192; |
||||||
|
uint16 constant pT1 = 4 + 256; |
||||||
|
uint16 constant pT2 = 4 + 320; |
||||||
|
uint16 constant pT3 = 4 + 384; |
||||||
|
uint16 constant pWxi = 4 + 448; |
||||||
|
uint16 constant pWxiw = 4 + 512; |
||||||
|
// Opening evaluations |
||||||
|
uint16 constant pEval_a = 4 + 576; |
||||||
|
uint16 constant pEval_b = 4 + 608; |
||||||
|
uint16 constant pEval_c = 4 + 640; |
||||||
|
uint16 constant pEval_s1 = 4 + 672; |
||||||
|
uint16 constant pEval_s2 = 4 + 704; |
||||||
|
uint16 constant pEval_zw = 4 + 736; |
||||||
|
|
||||||
|
// Memory data |
||||||
|
// Challenges |
||||||
|
uint16 constant pAlpha = 0; |
||||||
|
uint16 constant pBeta = 32; |
||||||
|
uint16 constant pGamma = 64; |
||||||
|
uint16 constant pXi = 96; |
||||||
|
uint16 constant pXin = 128; |
||||||
|
uint16 constant pBetaXi = 160; |
||||||
|
uint16 constant pV1 = 192; |
||||||
|
uint16 constant pV2 = 224; |
||||||
|
uint16 constant pV3 = 256; |
||||||
|
uint16 constant pV4 = 288; |
||||||
|
uint16 constant pV5 = 320; |
||||||
|
uint16 constant pU = 352; |
||||||
|
|
||||||
|
uint16 constant pPI = 384; |
||||||
|
uint16 constant pEval_r0 = 416; |
||||||
|
uint16 constant pD = 448; |
||||||
|
uint16 constant pF = 512; |
||||||
|
uint16 constant pE = 576; |
||||||
|
uint16 constant pTmp = 640; |
||||||
|
uint16 constant pAlpha2 = 704; |
||||||
|
uint16 constant pZh = 736; |
||||||
|
uint16 constant pZhInv = 768; |
||||||
|
|
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
uint16 constant pEval_l<%=i%> = <%=768+i*32%>; |
||||||
|
<% } %> |
||||||
|
<% let pLastMem = 800+32*Math.max(nPublic,1) %> |
||||||
|
|
||||||
|
uint16 constant lastMem = <%=pLastMem%>; |
||||||
|
|
||||||
|
function verifyProof(uint256[24] calldata _proof, uint256[<%=nPublic%>] calldata _pubSignals) public view returns (bool) { |
||||||
|
assembly { |
||||||
|
///////// |
||||||
|
// Computes the inverse using the extended euclidean algorithm |
||||||
|
///////// |
||||||
|
function inverse(a, q) -> inv { |
||||||
|
let t := 0 |
||||||
|
let newt := 1 |
||||||
|
let r := q |
||||||
|
let newr := a |
||||||
|
let quotient |
||||||
|
let aux |
||||||
|
|
||||||
|
for { } newr { } { |
||||||
|
quotient := sdiv(r, newr) |
||||||
|
aux := sub(t, mul(quotient, newt)) |
||||||
|
t:= newt |
||||||
|
newt:= aux |
||||||
|
|
||||||
|
aux := sub(r,mul(quotient, newr)) |
||||||
|
r := newr |
||||||
|
newr := aux |
||||||
|
} |
||||||
|
|
||||||
|
if gt(r, 1) { revert(0,0) } |
||||||
|
if slt(t, 0) { t:= add(t, q) } |
||||||
|
|
||||||
|
inv := t |
||||||
|
} |
||||||
|
|
||||||
|
/////// |
||||||
|
// Computes the inverse of an array of values |
||||||
|
// See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations |
||||||
|
////// |
||||||
|
function inverseArray(pVals, n) { |
||||||
|
|
||||||
|
let pAux := mload(0x40) // Point to the next free position |
||||||
|
let pIn := pVals |
||||||
|
let lastPIn := add(pVals, mul(n, 32)) // Read n elemnts |
||||||
|
let acc := mload(pIn) // Read the first element |
||||||
|
pIn := add(pIn, 32) // Point to the second element |
||||||
|
let inv |
||||||
|
|
||||||
|
|
||||||
|
for { } lt(pIn, lastPIn) { |
||||||
|
pAux := add(pAux, 32) |
||||||
|
pIn := add(pIn, 32) |
||||||
|
} |
||||||
|
{ |
||||||
|
mstore(pAux, acc) |
||||||
|
acc := mulmod(acc, mload(pIn), q) |
||||||
|
} |
||||||
|
acc := inverse(acc, q) |
||||||
|
|
||||||
|
// At this point pAux pint to the next free position we substract 1 to point to the last used |
||||||
|
pAux := sub(pAux, 32) |
||||||
|
// pIn points to the n+1 element, we substract to point to n |
||||||
|
pIn := sub(pIn, 32) |
||||||
|
lastPIn := pVals // We don't process the first element |
||||||
|
for { } gt(pIn, lastPIn) { |
||||||
|
pAux := sub(pAux, 32) |
||||||
|
pIn := sub(pIn, 32) |
||||||
|
} |
||||||
|
{ |
||||||
|
inv := mulmod(acc, mload(pAux), q) |
||||||
|
acc := mulmod(acc, mload(pIn), q) |
||||||
|
mstore(pIn, inv) |
||||||
|
} |
||||||
|
// pIn points to first element, we just set it. |
||||||
|
mstore(pIn, acc) |
||||||
|
} |
||||||
|
|
||||||
|
function checkField(v) { |
||||||
|
if iszero(lt(v, q)) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function checkInput() { |
||||||
|
checkField(calldataload(pEval_a)) |
||||||
|
checkField(calldataload(pEval_b)) |
||||||
|
checkField(calldataload(pEval_c)) |
||||||
|
checkField(calldataload(pEval_s1)) |
||||||
|
checkField(calldataload(pEval_s2)) |
||||||
|
checkField(calldataload(pEval_zw)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateChallenges(pMem, pPublic) { |
||||||
|
let beta |
||||||
|
let aux |
||||||
|
|
||||||
|
let mIn := mload(0x40) // Pointer to the next free memory position |
||||||
|
|
||||||
|
// Compute challenge.beta & challenge.gamma |
||||||
|
mstore(mIn, Qmx) |
||||||
|
mstore(add(mIn, 32), Qmy) |
||||||
|
mstore(add(mIn, 64), Qlx) |
||||||
|
mstore(add(mIn, 96), Qly) |
||||||
|
mstore(add(mIn, 128), Qrx) |
||||||
|
mstore(add(mIn, 160), Qry) |
||||||
|
mstore(add(mIn, 192), Qox) |
||||||
|
mstore(add(mIn, 224), Qoy) |
||||||
|
mstore(add(mIn, 256), Qcx) |
||||||
|
mstore(add(mIn, 288), Qcy) |
||||||
|
mstore(add(mIn, 320), S1x) |
||||||
|
mstore(add(mIn, 352), S1y) |
||||||
|
mstore(add(mIn, 384), S2x) |
||||||
|
mstore(add(mIn, 416), S2y) |
||||||
|
mstore(add(mIn, 448), S3x) |
||||||
|
mstore(add(mIn, 480), S3y) |
||||||
|
|
||||||
|
<%for (let i=0; i<nPublic;i++) {%> |
||||||
|
mstore(add(mIn, <%= 512 + i*32 %>), calldataload(add(pPublic, <%=i*32%>))) |
||||||
|
<%}%> |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 0 %> ), calldataload(pA)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 32 %> ), calldataload(add(pA, 32))) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 64 %> ), calldataload(pB)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 96 %> ), calldataload(add(pB, 32))) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 128 %> ), calldataload(pC)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 160 %> ), calldataload(add(pC, 32))) |
||||||
|
|
||||||
|
beta := mod(keccak256(mIn, <%= 704 + 32 * nPublic %>), q) |
||||||
|
mstore(add(pMem, pBeta), beta) |
||||||
|
|
||||||
|
// challenges.gamma |
||||||
|
mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q)) |
||||||
|
|
||||||
|
// challenges.alpha |
||||||
|
mstore(mIn, mload(add(pMem, pBeta))) |
||||||
|
mstore(add(mIn, 32), mload(add(pMem, pGamma))) |
||||||
|
mstore(add(mIn, 64), calldataload(pZ)) |
||||||
|
mstore(add(mIn, 96), calldataload(add(pZ, 32))) |
||||||
|
|
||||||
|
aux := mod(keccak256(mIn, 128), q) |
||||||
|
mstore(add(pMem, pAlpha), aux) |
||||||
|
mstore(add(pMem, pAlpha2), mulmod(aux, aux, q)) |
||||||
|
|
||||||
|
// challenges.xi |
||||||
|
mstore(mIn, aux) |
||||||
|
mstore(add(mIn, 32), calldataload(pT1)) |
||||||
|
mstore(add(mIn, 64), calldataload(add(pT1, 32))) |
||||||
|
mstore(add(mIn, 96), calldataload(pT2)) |
||||||
|
mstore(add(mIn, 128), calldataload(add(pT2, 32))) |
||||||
|
mstore(add(mIn, 160), calldataload(pT3)) |
||||||
|
mstore(add(mIn, 192), calldataload(add(pT3, 32))) |
||||||
|
|
||||||
|
aux := mod(keccak256(mIn, 224), q) |
||||||
|
mstore( add(pMem, pXi), aux) |
||||||
|
|
||||||
|
// challenges.v |
||||||
|
mstore(mIn, aux) |
||||||
|
mstore(add(mIn, 32), calldataload(pEval_a)) |
||||||
|
mstore(add(mIn, 64), calldataload(pEval_b)) |
||||||
|
mstore(add(mIn, 96), calldataload(pEval_c)) |
||||||
|
mstore(add(mIn, 128), calldataload(pEval_s1)) |
||||||
|
mstore(add(mIn, 160), calldataload(pEval_s2)) |
||||||
|
mstore(add(mIn, 192), calldataload(pEval_zw)) |
||||||
|
|
||||||
|
let v1 := mod(keccak256(mIn, 224), q) |
||||||
|
mstore(add(pMem, pV1), v1) |
||||||
|
|
||||||
|
// challenges.beta * challenges.xi |
||||||
|
mstore(add(pMem, pBetaXi), mulmod(beta, aux, q)) |
||||||
|
|
||||||
|
// challenges.xi^n |
||||||
|
<%for (let i=0; i<power;i++) {%> |
||||||
|
aux:= mulmod(aux, aux, q) |
||||||
|
<%}%> |
||||||
|
mstore(add(pMem, pXin), aux) |
||||||
|
|
||||||
|
// Zh |
||||||
|
aux:= mod(add(sub(aux, 1), q), q) |
||||||
|
mstore(add(pMem, pZh), aux) |
||||||
|
mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols |
||||||
|
|
||||||
|
// challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5 |
||||||
|
aux := mulmod(v1, v1, q) |
||||||
|
mstore(add(pMem, pV2), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV3), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV4), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV5), aux) |
||||||
|
|
||||||
|
// challenges.u |
||||||
|
mstore(mIn, calldataload(pWxi)) |
||||||
|
mstore(add(mIn, 32), calldataload(add(pWxi, 32))) |
||||||
|
mstore(add(mIn, 64), calldataload(pWxiw)) |
||||||
|
mstore(add(mIn, 96), calldataload(add(pWxiw, 32))) |
||||||
|
|
||||||
|
mstore(add(pMem, pU), mod(keccak256(mIn, 128), q)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateLagrange(pMem) { |
||||||
|
let w := 1 |
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l<%=i%>), |
||||||
|
mulmod( |
||||||
|
n, |
||||||
|
mod( |
||||||
|
add( |
||||||
|
sub( |
||||||
|
mload(add(pMem, pXi)), |
||||||
|
w |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% if (i<Math.max(nPublic, 1)) { %> |
||||||
|
w := mulmod(w, w1, q) |
||||||
|
<% } %> |
||||||
|
<% } %> |
||||||
|
|
||||||
|
inverseArray(add(pMem, pZhInv), <%=Math.max(nPublic, 1)+1%> ) |
||||||
|
|
||||||
|
let zh := mload(add(pMem, pZh)) |
||||||
|
w := 1 |
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
<% if (i==1) { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l1 ), |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l1 )), |
||||||
|
zh, |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% } else { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l<%=i%>), |
||||||
|
mulmod( |
||||||
|
w, |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l<%=i%>)), |
||||||
|
zh, |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% } %> |
||||||
|
<% if (i<Math.max(nPublic, 1)) { %> |
||||||
|
w := mulmod(w, w1, q) |
||||||
|
<% } %> |
||||||
|
<% } %> |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function calculatePI(pMem, pPub) { |
||||||
|
let pl := 0 |
||||||
|
|
||||||
|
<% for (let i=0; i<nPublic; i++) { %> |
||||||
|
pl := mod( |
||||||
|
add( |
||||||
|
sub( |
||||||
|
pl, |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l<%=i+1%>)), |
||||||
|
calldataload(add(pPub, <%=i*32%>)), |
||||||
|
q |
||||||
|
) |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
<% } %> |
||||||
|
|
||||||
|
mstore(add(pMem, pPI), pl) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateR0(pMem) { |
||||||
|
let e1 := mload(add(pMem, pPI)) |
||||||
|
|
||||||
|
let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q) |
||||||
|
|
||||||
|
let e3a := addmod( |
||||||
|
calldataload(pEval_a), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), |
||||||
|
q) |
||||||
|
e3a := addmod(e3a, mload(add(pMem, pGamma)), q) |
||||||
|
|
||||||
|
let e3b := addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), |
||||||
|
q) |
||||||
|
e3b := addmod(e3b, mload(add(pMem, pGamma)), q) |
||||||
|
|
||||||
|
let e3c := addmod( |
||||||
|
calldataload(pEval_c), |
||||||
|
mload(add(pMem, pGamma)), |
||||||
|
q) |
||||||
|
|
||||||
|
let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q) |
||||||
|
e3 := mulmod(e3, calldataload(pEval_zw), q) |
||||||
|
e3 := mulmod(e3, mload(add(pMem, pAlpha)), q) |
||||||
|
|
||||||
|
let r0 := addmod(e1, mod(sub(q, e2), q), q) |
||||||
|
r0 := addmod(r0, mod(sub(q, e3), q), q) |
||||||
|
|
||||||
|
mstore(add(pMem, pEval_r0) , r0) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_set(pR, pP) { |
||||||
|
mstore(pR, mload(pP)) |
||||||
|
mstore(add(pR, 32), mload(add(pP,32))) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_setC(pR, x, y) { |
||||||
|
mstore(pR, x) |
||||||
|
mstore(add(pR, 32), y) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_calldataSet(pR, pP) { |
||||||
|
mstore(pR, calldataload(pP)) |
||||||
|
mstore(add(pR, 32), calldataload(add(pP, 32))) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_acc(pR, pP) { |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, mload(pR)) |
||||||
|
mstore(add(mIn,32), mload(add(pR, 32))) |
||||||
|
mstore(add(mIn,64), mload(pP)) |
||||||
|
mstore(add(mIn,96), mload(add(pP, 32))) |
||||||
|
|
||||||
|
let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulAcc(pR, pP, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, mload(pP)) |
||||||
|
mstore(add(mIn,32), mload(add(pP, 32))) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
mstore(add(mIn,64), mload(pR)) |
||||||
|
mstore(add(mIn,96), mload(add(pR, 32))) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulAccC(pR, x, y, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, x) |
||||||
|
mstore(add(mIn,32), y) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
mstore(add(mIn,64), mload(pR)) |
||||||
|
mstore(add(mIn,96), mload(add(pR, 32))) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulSetC(pR, x, y, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, x) |
||||||
|
mstore(add(mIn,32), y) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulSet(pR, pP, s) { |
||||||
|
g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateD(pMem) { |
||||||
|
let _pD:= add(pMem, pD) |
||||||
|
let gamma := mload(add(pMem, pGamma)) |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes) |
||||||
|
|
||||||
|
g1_setC(_pD, Qcx, Qcy) |
||||||
|
g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q)) |
||||||
|
g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a)) |
||||||
|
g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b)) |
||||||
|
g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c)) |
||||||
|
|
||||||
|
let betaxi := mload(add(pMem, pBetaXi)) |
||||||
|
let val1 := addmod( |
||||||
|
addmod(calldataload(pEval_a), betaxi, q), |
||||||
|
gamma, q) |
||||||
|
|
||||||
|
let val2 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(betaxi, k1, q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
let val3 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_c), |
||||||
|
mulmod(betaxi, k2, q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
let d2a := mulmod( |
||||||
|
mulmod(mulmod(val1, val2, q), val3, q), |
||||||
|
mload(add(pMem, pAlpha)), |
||||||
|
q |
||||||
|
) |
||||||
|
|
||||||
|
let d2b := mulmod( |
||||||
|
mload(add(pMem, pEval_l1)), |
||||||
|
mload(add(pMem, pAlpha2)), |
||||||
|
q |
||||||
|
) |
||||||
|
|
||||||
|
// We'll use mIn to save d2 |
||||||
|
g1_calldataSet(add(mIn, 192), pZ) |
||||||
|
g1_mulSet( |
||||||
|
mIn, |
||||||
|
add(mIn, 192), |
||||||
|
addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q)) |
||||||
|
|
||||||
|
|
||||||
|
val1 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_a), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
val2 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
val3 := mulmod( |
||||||
|
mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q), |
||||||
|
calldataload(pEval_zw), q) |
||||||
|
|
||||||
|
|
||||||
|
// We'll use mIn + 64 to save d3 |
||||||
|
g1_mulSetC( |
||||||
|
add(mIn, 64), |
||||||
|
S3x, |
||||||
|
S3y, |
||||||
|
mulmod(mulmod(val1, val2, q), val3, q)) |
||||||
|
|
||||||
|
// We'll use mIn + 128 to save d4 |
||||||
|
g1_calldataSet(add(mIn, 128), pT1) |
||||||
|
|
||||||
|
g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin))) |
||||||
|
let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q) |
||||||
|
g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2) |
||||||
|
|
||||||
|
g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh))) |
||||||
|
|
||||||
|
mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf)) |
||||||
|
mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf)) |
||||||
|
g1_acc(_pD, mIn) |
||||||
|
g1_acc(_pD, add(mIn, 64)) |
||||||
|
g1_acc(_pD, add(mIn, 128)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateF(pMem) { |
||||||
|
let p := add(pMem, pF) |
||||||
|
|
||||||
|
g1_set(p, add(pMem, pD)) |
||||||
|
g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1))) |
||||||
|
g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2))) |
||||||
|
g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3))) |
||||||
|
g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4))) |
||||||
|
g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5))) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateE(pMem) { |
||||||
|
let s := mod(sub(q, mload(add(pMem, pEval_r0))), q) |
||||||
|
|
||||||
|
s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q) |
||||||
|
|
||||||
|
g1_mulSetC(add(pMem, pE), G1x, G1y, s) |
||||||
|
} |
||||||
|
|
||||||
|
function checkPairing(pMem) -> isOk { |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw |
||||||
|
|
||||||
|
let _pWxi := add(mIn, 384) |
||||||
|
let _pWxiw := add(mIn, 448) |
||||||
|
let _aux := add(mIn, 512) |
||||||
|
|
||||||
|
g1_calldataSet(_pWxi, pWxi) |
||||||
|
g1_calldataSet(_pWxiw, pWxiw) |
||||||
|
|
||||||
|
// A1 |
||||||
|
g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU))) |
||||||
|
g1_acc(mIn, _pWxi) |
||||||
|
mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf)) |
||||||
|
|
||||||
|
// [X]_2 |
||||||
|
mstore(add(mIn,64), X2x2) |
||||||
|
mstore(add(mIn,96), X2x1) |
||||||
|
mstore(add(mIn,128), X2y2) |
||||||
|
mstore(add(mIn,160), X2y1) |
||||||
|
|
||||||
|
// B1 |
||||||
|
g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi))) |
||||||
|
|
||||||
|
let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q) |
||||||
|
s := mulmod(s, w1, q) |
||||||
|
g1_mulSet(_aux, _pWxiw, s) |
||||||
|
g1_acc(add(mIn, 192), _aux) |
||||||
|
g1_acc(add(mIn, 192), add(pMem, pF)) |
||||||
|
mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf)) |
||||||
|
g1_acc(add(mIn, 192), add(pMem, pE)) |
||||||
|
|
||||||
|
// [1]_2 |
||||||
|
mstore(add(mIn,256), G2x2) |
||||||
|
mstore(add(mIn,288), G2x1) |
||||||
|
mstore(add(mIn,320), G2y2) |
||||||
|
mstore(add(mIn,352), G2y1) |
||||||
|
|
||||||
|
let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20) |
||||||
|
|
||||||
|
isOk := and(success, mload(mIn)) |
||||||
|
} |
||||||
|
|
||||||
|
let pMem := mload(0x40) |
||||||
|
mstore(0x40, add(pMem, lastMem)) |
||||||
|
|
||||||
|
checkInput() |
||||||
|
calculateChallenges(pMem, _pubSignals) |
||||||
|
calculateLagrange(pMem) |
||||||
|
calculatePI(pMem, _pubSignals) |
||||||
|
calculateR0(pMem) |
||||||
|
calculateD(pMem) |
||||||
|
calculateF(pMem) |
||||||
|
calculateE(pMem) |
||||||
|
let isValid := checkPairing(pMem) |
||||||
|
|
||||||
|
mstore(0x40, sub(pMem, lastMem)) |
||||||
|
mstore(0, isValid) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
const logger = { |
||||||
|
info: (...args) => console.log(...args), |
||||||
|
debug: (...args) => console.log(...args) |
||||||
|
}; |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('circuit-compiler', 'generateR1cs', 'circuits/semaphore.circom'); |
||||||
|
|
||||||
|
const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW"; |
||||||
|
// @ts-ignore
|
||||||
|
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', { encoding: null }); |
||||||
|
// @ts-ignore
|
||||||
|
const r1cs = new Uint8Array(r1csBuffer); |
||||||
|
const zkey_0 = { type: "mem" }; |
||||||
|
const zkey_1 = { type: "mem" }; |
||||||
|
const zkey_final = { type: "mem" }; |
||||||
|
|
||||||
|
console.log('newZkey') |
||||||
|
await snarkjs.zKey.newZKey(r1cs, ptau_final, zkey_0); |
||||||
|
|
||||||
|
console.log('contribute') |
||||||
|
await snarkjs.zKey.contribute(zkey_0, zkey_1, "p2_C1", "pa_Entropy1"); |
||||||
|
|
||||||
|
console.log('beacon') |
||||||
|
await snarkjs.zKey.beacon(zkey_1, zkey_final, "B3", "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", 10); |
||||||
|
|
||||||
|
console.log('verifyFromR1cs') |
||||||
|
const verifyFromR1csResult = await snarkjs.zKey.verifyFromR1cs(r1cs, ptau_final, zkey_final); |
||||||
|
console.assert(verifyFromR1csResult); |
||||||
|
|
||||||
|
console.log('verifyFromInit') |
||||||
|
const verifyFromInit = await snarkjs.zKey.verifyFromInit(zkey_0, ptau_final, zkey_final); |
||||||
|
console.assert(verifyFromInit); |
||||||
|
|
||||||
|
console.log('exportVerificationKey') |
||||||
|
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) |
||||||
|
await remix.call('fileManager', 'writeFile', './zk/keys/groth16/verification_key.json', JSON.stringify(vKey, null, 2)) |
||||||
|
|
||||||
|
console.log('save zkey_final') |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('fileManager', 'writeFile', './zk/keys/groth16/zkey_final.txt', (zkey_final as any).data, { encoding: null }) |
||||||
|
|
||||||
|
console.log('setup done.') |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
@ -0,0 +1,114 @@ |
|||||||
|
import { ethers, BigNumber } from 'ethers' |
||||||
|
import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree" |
||||||
|
import { poseidon } from "circomlibjs" // v0.0.8
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
const logger = { |
||||||
|
info: (...args) => console.log(...args), |
||||||
|
debug: (...args) => console.log(...args), |
||||||
|
error: (...args) => console.error(...args), |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a keccak256 hash of a message compatible with the SNARK scalar modulus. |
||||||
|
* @param message The message to be hashed. |
||||||
|
* @returns The message digest. |
||||||
|
*/ |
||||||
|
function hash(message: any): bigint { |
||||||
|
message = BigNumber.from(message).toTwos(256).toHexString() |
||||||
|
message = ethers.utils.zeroPad(message, 32) |
||||||
|
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) |
||||||
|
} |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', true); |
||||||
|
// @ts-ignore
|
||||||
|
const r1cs = new Uint8Array(r1csBuffer); |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('circuit-compiler', 'compile', 'circuits/semaphore.circom'); |
||||||
|
// @ts-ignore
|
||||||
|
const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.wasm', true); |
||||||
|
// @ts-ignore
|
||||||
|
const wasm = new Uint8Array(wasmBuffer);
|
||||||
|
|
||||||
|
const zkey_final = { |
||||||
|
type: "mem", |
||||||
|
// @ts-ignore
|
||||||
|
data: new Uint8Array(await remix.call('fileManager', 'readFile', './zk/keys/groth16/zkey_final.txt', { encoding: null })) |
||||||
|
} |
||||||
|
const wtns = { type: "mem" };
|
||||||
|
|
||||||
|
const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/groth16/verification_key.json')) |
||||||
|
|
||||||
|
// build list of identity commitments
|
||||||
|
const secrets = [] |
||||||
|
const identityCommitments = [] |
||||||
|
for (let k = 0; k < 2; k++) { |
||||||
|
const identityTrapdoor = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) |
||||||
|
const identityNullifier = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) |
||||||
|
secrets.push({identityTrapdoor, identityNullifier}) |
||||||
|
|
||||||
|
const secret = poseidon([identityNullifier, identityTrapdoor]) |
||||||
|
const identityCommitment = poseidon([secret]) |
||||||
|
identityCommitments.push(identityCommitment) |
||||||
|
} |
||||||
|
//console.log('incremental tree', identityCommitments.map((x) => x.toString()))
|
||||||
|
|
||||||
|
let tree |
||||||
|
|
||||||
|
try { |
||||||
|
tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, identityCommitments) // Binary tree.
|
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
return |
||||||
|
} |
||||||
|
const index = tree.indexOf(identityCommitments[0]) |
||||||
|
|
||||||
|
console.log(index.toString()) |
||||||
|
|
||||||
|
const proof1 = tree.createProof(0) |
||||||
|
|
||||||
|
console.log('prepare signals for id ', identityCommitments[0].toString(), tree.indexOf(identityCommitments[0]), proof1.siblings.map((x)=> x.toString())) |
||||||
|
|
||||||
|
const signals = { |
||||||
|
identityTrapdoor: secrets[0].identityTrapdoor, |
||||||
|
identityNullifier: secrets[0].identityNullifier, |
||||||
|
treePathIndices: proof1.pathIndices, |
||||||
|
treeSiblings: proof1.siblings, |
||||||
|
externalNullifier: hash(42), |
||||||
|
signalHash: hash(ethers.utils.formatBytes32String("Hello World")) |
||||||
|
} |
||||||
|
|
||||||
|
console.log('calculate') |
||||||
|
await snarkjs.wtns.calculate(signals, wasm, wtns); |
||||||
|
|
||||||
|
console.log('check') |
||||||
|
await snarkjs.wtns.check(r1cs, wtns, logger); |
||||||
|
|
||||||
|
console.log('prove') |
||||||
|
const { proof, publicSignals } = await snarkjs.groth16.prove(zkey_final, wtns); |
||||||
|
|
||||||
|
const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof, logger); |
||||||
|
console.log('zk proof validity', verified); |
||||||
|
proof1.root.toString() === publicSignals[0] ? console.log('merkle proof valid') : console.log('merkle proof invalid') |
||||||
|
|
||||||
|
const templates = { |
||||||
|
groth16: await remix.call('fileManager', 'readFile', 'templates/groth16_verifier.sol.ejs') |
||||||
|
} |
||||||
|
const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) |
||||||
|
|
||||||
|
await remix.call('fileManager', 'writeFile', './zk/build/groth16/zk_verifier.sol', solidityContract) |
||||||
|
await remix.call('fileManager', 'writeFile', 'zk/build/groth16/input.json', JSON.stringify({ |
||||||
|
_pA: [proof.pi_a[0], proof.pi_a[1]], |
||||||
|
_pB: [[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]], |
||||||
|
_pC: [proof.pi_c[0], proof.pi_c[1]], |
||||||
|
_pubSignals: publicSignals |
||||||
|
}, null, 2)) |
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
@ -0,0 +1,37 @@ |
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
const logger = { |
||||||
|
info: (...args) => console.log(...args), |
||||||
|
debug: (...args) => console.log(...args) |
||||||
|
}; |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('circuit-compiler', 'generateR1cs', 'circuits/semaphore.circom'); |
||||||
|
|
||||||
|
const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmciCq5JcZQyTLvC9GRanrLBi82ZmSriq1Fr5zANkGHebf"; |
||||||
|
// @ts-ignore
|
||||||
|
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', { encoding: null }); |
||||||
|
// @ts-ignore
|
||||||
|
const r1cs = new Uint8Array(r1csBuffer); |
||||||
|
const zkey_final = { type: "mem" }; |
||||||
|
|
||||||
|
console.log('plonk setup') |
||||||
|
await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final) |
||||||
|
|
||||||
|
console.log('exportVerificationKey') |
||||||
|
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final) |
||||||
|
await remix.call('fileManager', 'writeFile', './zk/keys/plonk/verification_key.json', JSON.stringify(vKey, null, 2)) |
||||||
|
|
||||||
|
console.log('save zkey_final') |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('fileManager', 'writeFile', './zk/keys/plonk/zkey_final.txt', (zkey_final as any).data, { encoding: null }) |
||||||
|
|
||||||
|
console.log('setup done.') |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
@ -0,0 +1,139 @@ |
|||||||
|
import { ethers, BigNumber } from 'ethers' |
||||||
|
import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree" |
||||||
|
import { poseidon } from "circomlibjs" // v0.0.8
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const snarkjs = require('snarkjs'); |
||||||
|
|
||||||
|
const logger = { |
||||||
|
info: (...args) => console.log(...args), |
||||||
|
debug: (...args) => console.log(...args), |
||||||
|
error: (...args) => console.error(...args), |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a keccak256 hash of a message compatible with the SNARK scalar modulus. |
||||||
|
* @param message The message to be hashed. |
||||||
|
* @returns The message digest. |
||||||
|
*/ |
||||||
|
function hash(message: any): bigint { |
||||||
|
message = BigNumber.from(message).toTwos(256).toHexString() |
||||||
|
message = ethers.utils.zeroPad(message, 32) |
||||||
|
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8) |
||||||
|
} |
||||||
|
|
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
// @ts-ignore
|
||||||
|
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', true); |
||||||
|
// @ts-ignore
|
||||||
|
const r1cs = new Uint8Array(r1csBuffer); |
||||||
|
// @ts-ignore
|
||||||
|
await remix.call('circuit-compiler', 'compile', 'circuits/semaphore.circom'); |
||||||
|
// @ts-ignore
|
||||||
|
const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.wasm', true); |
||||||
|
// @ts-ignore
|
||||||
|
const wasm = new Uint8Array(wasmBuffer);
|
||||||
|
|
||||||
|
const zkey_final = { |
||||||
|
type: "mem", |
||||||
|
// @ts-ignore
|
||||||
|
data: new Uint8Array(await remix.call('fileManager', 'readFile', './zk/keys/plonk/zkey_final.txt', { encoding: null })) |
||||||
|
} |
||||||
|
const wtns = { type: "mem" };
|
||||||
|
|
||||||
|
const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/keys/plonk/verification_key.json')) |
||||||
|
|
||||||
|
// build list of identity commitments
|
||||||
|
const secrets = [] |
||||||
|
const identityCommitments = [] |
||||||
|
for (let k = 0; k < 2; k++) { |
||||||
|
const identityTrapdoor = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) |
||||||
|
const identityNullifier = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32))) |
||||||
|
secrets.push({identityTrapdoor, identityNullifier}) |
||||||
|
|
||||||
|
const secret = poseidon([identityNullifier, identityTrapdoor]) |
||||||
|
const identityCommitment = poseidon([secret]) |
||||||
|
identityCommitments.push(identityCommitment) |
||||||
|
} |
||||||
|
//console.log('incremental tree', identityCommitments.map((x) => x.toString()))
|
||||||
|
|
||||||
|
let tree |
||||||
|
|
||||||
|
try { |
||||||
|
tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, identityCommitments) // Binary tree.
|
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
return |
||||||
|
} |
||||||
|
const index = tree.indexOf(identityCommitments[0]) |
||||||
|
|
||||||
|
console.log(index.toString()) |
||||||
|
|
||||||
|
const proof1 = tree.createProof(0) |
||||||
|
|
||||||
|
console.log('prepare signals for id ', identityCommitments[0].toString(), tree.indexOf(identityCommitments[0]), proof1.siblings.map((x)=> x.toString())) |
||||||
|
|
||||||
|
const signals = { |
||||||
|
identityTrapdoor: secrets[0].identityTrapdoor, |
||||||
|
identityNullifier: secrets[0].identityNullifier, |
||||||
|
treePathIndices: proof1.pathIndices, |
||||||
|
treeSiblings: proof1.siblings, |
||||||
|
externalNullifier: hash(42), |
||||||
|
signalHash: hash(ethers.utils.formatBytes32String("Hello World")) |
||||||
|
} |
||||||
|
|
||||||
|
console.log('calculate') |
||||||
|
await snarkjs.wtns.calculate(signals, wasm, wtns); |
||||||
|
|
||||||
|
console.log('check') |
||||||
|
await snarkjs.wtns.check(r1cs, wtns, logger); |
||||||
|
|
||||||
|
console.log('prove') |
||||||
|
const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns); |
||||||
|
|
||||||
|
const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof, logger); |
||||||
|
console.log('zk proof validity', verified); |
||||||
|
proof1.root.toString() === publicSignals[0] ? console.log('merkle proof valid') : console.log('merkle proof invalid') |
||||||
|
|
||||||
|
const templates = { |
||||||
|
plonk: await remix.call('fileManager', 'readFile', 'templates/plonk_verifier.sol.ejs') |
||||||
|
} |
||||||
|
const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates) |
||||||
|
|
||||||
|
await remix.call('fileManager', 'writeFile', './zk/build/plonk/zk_verifier.sol', solidityContract) |
||||||
|
await remix.call('fileManager', 'writeFile', 'zk/build/plonk/input.json', JSON.stringify({ |
||||||
|
_proof: [ |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.A[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.B[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.B[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.C[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.C[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Z[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Z[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T1[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T1[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T2[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T2[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T3[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.T3[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxi[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxi[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxiw[0]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.Wxiw[1]).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_a).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_b).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_c).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_s1).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_s2).toHexString(), 32), |
||||||
|
ethers.utils.hexZeroPad(ethers.BigNumber.from(proof.eval_zw).toHexString(), 32), |
||||||
|
], |
||||||
|
_pubSignals: publicSignals |
||||||
|
}, null, 2)) |
||||||
|
|
||||||
|
console.log('proof done.') |
||||||
|
} catch (e) { |
||||||
|
console.error(e.message) |
||||||
|
} |
||||||
|
})() |
@ -0,0 +1,710 @@ |
|||||||
|
// SPDX-License-Identifier: GPL-3.0 |
||||||
|
/* |
||||||
|
Copyright 2021 0KIMS association. |
||||||
|
|
||||||
|
This file is generated with [snarkJS](https://github.com/iden3/snarkjs). |
||||||
|
|
||||||
|
snarkJS is a free software: you can redistribute it and/or modify it |
||||||
|
under the terms of the GNU General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
snarkJS is distributed in the hope that it will be useful, but WITHOUT |
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
||||||
|
License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License |
||||||
|
along with snarkJS. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
pragma solidity >=0.7.0 <0.9.0; |
||||||
|
|
||||||
|
import "hardhat/console.sol"; |
||||||
|
|
||||||
|
contract PlonkVerifier { |
||||||
|
// Omega |
||||||
|
uint256 constant w1 = <%=w%>; |
||||||
|
// Scalar field size |
||||||
|
uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; |
||||||
|
// Base field size |
||||||
|
uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583; |
||||||
|
|
||||||
|
// [1]_1 |
||||||
|
uint256 constant G1x = 1; |
||||||
|
uint256 constant G1y = 2; |
||||||
|
// [1]_2 |
||||||
|
uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; |
||||||
|
uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; |
||||||
|
uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; |
||||||
|
uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; |
||||||
|
|
||||||
|
// Verification Key data |
||||||
|
uint32 constant n = <%=2**power%>; |
||||||
|
uint16 constant nPublic = <%=nPublic%>; |
||||||
|
uint16 constant nLagrange = <%=Math.max(nPublic, 1)%>; |
||||||
|
|
||||||
|
uint256 constant Qmx = <%=Qm[0]%>; |
||||||
|
uint256 constant Qmy = <%=Qm[0] == "0" ? "0" : Qm[1]%>; |
||||||
|
uint256 constant Qlx = <%=Ql[0]%>; |
||||||
|
uint256 constant Qly = <%=Ql[0] == "0" ? "0" : Ql[1]%>; |
||||||
|
uint256 constant Qrx = <%=Qr[0]%>; |
||||||
|
uint256 constant Qry = <%=Qr[0] == "0" ? "0" : Qr[1]%>; |
||||||
|
uint256 constant Qox = <%=Qo[0]%>; |
||||||
|
uint256 constant Qoy = <%=Qo[0] == "0" ? "0" : Qo[1]%>; |
||||||
|
uint256 constant Qcx = <%=Qc[0]%>; |
||||||
|
uint256 constant Qcy = <%=Qc[0] == "0" ? "0" : Qc[1]%>; |
||||||
|
uint256 constant S1x = <%=S1[0]%>; |
||||||
|
uint256 constant S1y = <%=S1[0] == "0" ? "0" : S1[1]%>; |
||||||
|
uint256 constant S2x = <%=S2[0]%>; |
||||||
|
uint256 constant S2y = <%=S2[0] == "0" ? "0" : S2[1]%>; |
||||||
|
uint256 constant S3x = <%=S3[0]%>; |
||||||
|
uint256 constant S3y = <%=S3[0] == "0" ? "0" : S3[1]%>; |
||||||
|
uint256 constant k1 = <%=k1%>; |
||||||
|
uint256 constant k2 = <%=k2%>; |
||||||
|
uint256 constant X2x1 = <%=X_2[0][0]%>; |
||||||
|
uint256 constant X2x2 = <%=X_2[0][1]%>; |
||||||
|
uint256 constant X2y1 = <%=X_2[1][0]%>; |
||||||
|
uint256 constant X2y2 = <%=X_2[1][1]%>; |
||||||
|
|
||||||
|
// Proof calldata |
||||||
|
// Byte offset of every parameter of the calldata |
||||||
|
// Polynomial commitments |
||||||
|
uint16 constant pA = 4 + 0; |
||||||
|
uint16 constant pB = 4 + 64; |
||||||
|
uint16 constant pC = 4 + 128; |
||||||
|
uint16 constant pZ = 4 + 192; |
||||||
|
uint16 constant pT1 = 4 + 256; |
||||||
|
uint16 constant pT2 = 4 + 320; |
||||||
|
uint16 constant pT3 = 4 + 384; |
||||||
|
uint16 constant pWxi = 4 + 448; |
||||||
|
uint16 constant pWxiw = 4 + 512; |
||||||
|
// Opening evaluations |
||||||
|
uint16 constant pEval_a = 4 + 576; |
||||||
|
uint16 constant pEval_b = 4 + 608; |
||||||
|
uint16 constant pEval_c = 4 + 640; |
||||||
|
uint16 constant pEval_s1 = 4 + 672; |
||||||
|
uint16 constant pEval_s2 = 4 + 704; |
||||||
|
uint16 constant pEval_zw = 4 + 736; |
||||||
|
|
||||||
|
// Memory data |
||||||
|
// Challenges |
||||||
|
uint16 constant pAlpha = 0; |
||||||
|
uint16 constant pBeta = 32; |
||||||
|
uint16 constant pGamma = 64; |
||||||
|
uint16 constant pXi = 96; |
||||||
|
uint16 constant pXin = 128; |
||||||
|
uint16 constant pBetaXi = 160; |
||||||
|
uint16 constant pV1 = 192; |
||||||
|
uint16 constant pV2 = 224; |
||||||
|
uint16 constant pV3 = 256; |
||||||
|
uint16 constant pV4 = 288; |
||||||
|
uint16 constant pV5 = 320; |
||||||
|
uint16 constant pU = 352; |
||||||
|
|
||||||
|
uint16 constant pPI = 384; |
||||||
|
uint16 constant pEval_r0 = 416; |
||||||
|
uint16 constant pD = 448; |
||||||
|
uint16 constant pF = 512; |
||||||
|
uint16 constant pE = 576; |
||||||
|
uint16 constant pTmp = 640; |
||||||
|
uint16 constant pAlpha2 = 704; |
||||||
|
uint16 constant pZh = 736; |
||||||
|
uint16 constant pZhInv = 768; |
||||||
|
|
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
uint16 constant pEval_l<%=i%> = <%=768+i*32%>; |
||||||
|
<% } %> |
||||||
|
<% let pLastMem = 800+32*Math.max(nPublic,1) %> |
||||||
|
|
||||||
|
uint16 constant lastMem = <%=pLastMem%>; |
||||||
|
|
||||||
|
function verifyProof(uint256[24] calldata _proof, uint256[<%=nPublic%>] calldata _pubSignals) public view returns (bool) { |
||||||
|
assembly { |
||||||
|
///////// |
||||||
|
// Computes the inverse using the extended euclidean algorithm |
||||||
|
///////// |
||||||
|
function inverse(a, q) -> inv { |
||||||
|
let t := 0 |
||||||
|
let newt := 1 |
||||||
|
let r := q |
||||||
|
let newr := a |
||||||
|
let quotient |
||||||
|
let aux |
||||||
|
|
||||||
|
for { } newr { } { |
||||||
|
quotient := sdiv(r, newr) |
||||||
|
aux := sub(t, mul(quotient, newt)) |
||||||
|
t:= newt |
||||||
|
newt:= aux |
||||||
|
|
||||||
|
aux := sub(r,mul(quotient, newr)) |
||||||
|
r := newr |
||||||
|
newr := aux |
||||||
|
} |
||||||
|
|
||||||
|
if gt(r, 1) { revert(0,0) } |
||||||
|
if slt(t, 0) { t:= add(t, q) } |
||||||
|
|
||||||
|
inv := t |
||||||
|
} |
||||||
|
|
||||||
|
/////// |
||||||
|
// Computes the inverse of an array of values |
||||||
|
// See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations |
||||||
|
////// |
||||||
|
function inverseArray(pVals, n) { |
||||||
|
|
||||||
|
let pAux := mload(0x40) // Point to the next free position |
||||||
|
let pIn := pVals |
||||||
|
let lastPIn := add(pVals, mul(n, 32)) // Read n elemnts |
||||||
|
let acc := mload(pIn) // Read the first element |
||||||
|
pIn := add(pIn, 32) // Point to the second element |
||||||
|
let inv |
||||||
|
|
||||||
|
|
||||||
|
for { } lt(pIn, lastPIn) { |
||||||
|
pAux := add(pAux, 32) |
||||||
|
pIn := add(pIn, 32) |
||||||
|
} |
||||||
|
{ |
||||||
|
mstore(pAux, acc) |
||||||
|
acc := mulmod(acc, mload(pIn), q) |
||||||
|
} |
||||||
|
acc := inverse(acc, q) |
||||||
|
|
||||||
|
// At this point pAux pint to the next free position we substract 1 to point to the last used |
||||||
|
pAux := sub(pAux, 32) |
||||||
|
// pIn points to the n+1 element, we substract to point to n |
||||||
|
pIn := sub(pIn, 32) |
||||||
|
lastPIn := pVals // We don't process the first element |
||||||
|
for { } gt(pIn, lastPIn) { |
||||||
|
pAux := sub(pAux, 32) |
||||||
|
pIn := sub(pIn, 32) |
||||||
|
} |
||||||
|
{ |
||||||
|
inv := mulmod(acc, mload(pAux), q) |
||||||
|
acc := mulmod(acc, mload(pIn), q) |
||||||
|
mstore(pIn, inv) |
||||||
|
} |
||||||
|
// pIn points to first element, we just set it. |
||||||
|
mstore(pIn, acc) |
||||||
|
} |
||||||
|
|
||||||
|
function checkField(v) { |
||||||
|
if iszero(lt(v, q)) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function checkInput() { |
||||||
|
checkField(calldataload(pEval_a)) |
||||||
|
checkField(calldataload(pEval_b)) |
||||||
|
checkField(calldataload(pEval_c)) |
||||||
|
checkField(calldataload(pEval_s1)) |
||||||
|
checkField(calldataload(pEval_s2)) |
||||||
|
checkField(calldataload(pEval_zw)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateChallenges(pMem, pPublic) { |
||||||
|
let beta |
||||||
|
let aux |
||||||
|
|
||||||
|
let mIn := mload(0x40) // Pointer to the next free memory position |
||||||
|
|
||||||
|
// Compute challenge.beta & challenge.gamma |
||||||
|
mstore(mIn, Qmx) |
||||||
|
mstore(add(mIn, 32), Qmy) |
||||||
|
mstore(add(mIn, 64), Qlx) |
||||||
|
mstore(add(mIn, 96), Qly) |
||||||
|
mstore(add(mIn, 128), Qrx) |
||||||
|
mstore(add(mIn, 160), Qry) |
||||||
|
mstore(add(mIn, 192), Qox) |
||||||
|
mstore(add(mIn, 224), Qoy) |
||||||
|
mstore(add(mIn, 256), Qcx) |
||||||
|
mstore(add(mIn, 288), Qcy) |
||||||
|
mstore(add(mIn, 320), S1x) |
||||||
|
mstore(add(mIn, 352), S1y) |
||||||
|
mstore(add(mIn, 384), S2x) |
||||||
|
mstore(add(mIn, 416), S2y) |
||||||
|
mstore(add(mIn, 448), S3x) |
||||||
|
mstore(add(mIn, 480), S3y) |
||||||
|
|
||||||
|
<%for (let i=0; i<nPublic;i++) {%> |
||||||
|
mstore(add(mIn, <%= 512 + i*32 %>), calldataload(add(pPublic, <%=i*32%>))) |
||||||
|
<%}%> |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 0 %> ), calldataload(pA)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 32 %> ), calldataload(add(pA, 32))) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 64 %> ), calldataload(pB)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 96 %> ), calldataload(add(pB, 32))) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 128 %> ), calldataload(pC)) |
||||||
|
mstore(add(mIn, <%= 512 + nPublic*32 + 160 %> ), calldataload(add(pC, 32))) |
||||||
|
|
||||||
|
beta := mod(keccak256(mIn, <%= 704 + 32 * nPublic %>), q) |
||||||
|
mstore(add(pMem, pBeta), beta) |
||||||
|
|
||||||
|
// challenges.gamma |
||||||
|
mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q)) |
||||||
|
|
||||||
|
// challenges.alpha |
||||||
|
mstore(mIn, mload(add(pMem, pBeta))) |
||||||
|
mstore(add(mIn, 32), mload(add(pMem, pGamma))) |
||||||
|
mstore(add(mIn, 64), calldataload(pZ)) |
||||||
|
mstore(add(mIn, 96), calldataload(add(pZ, 32))) |
||||||
|
|
||||||
|
aux := mod(keccak256(mIn, 128), q) |
||||||
|
mstore(add(pMem, pAlpha), aux) |
||||||
|
mstore(add(pMem, pAlpha2), mulmod(aux, aux, q)) |
||||||
|
|
||||||
|
// challenges.xi |
||||||
|
mstore(mIn, aux) |
||||||
|
mstore(add(mIn, 32), calldataload(pT1)) |
||||||
|
mstore(add(mIn, 64), calldataload(add(pT1, 32))) |
||||||
|
mstore(add(mIn, 96), calldataload(pT2)) |
||||||
|
mstore(add(mIn, 128), calldataload(add(pT2, 32))) |
||||||
|
mstore(add(mIn, 160), calldataload(pT3)) |
||||||
|
mstore(add(mIn, 192), calldataload(add(pT3, 32))) |
||||||
|
|
||||||
|
aux := mod(keccak256(mIn, 224), q) |
||||||
|
mstore( add(pMem, pXi), aux) |
||||||
|
|
||||||
|
// challenges.v |
||||||
|
mstore(mIn, aux) |
||||||
|
mstore(add(mIn, 32), calldataload(pEval_a)) |
||||||
|
mstore(add(mIn, 64), calldataload(pEval_b)) |
||||||
|
mstore(add(mIn, 96), calldataload(pEval_c)) |
||||||
|
mstore(add(mIn, 128), calldataload(pEval_s1)) |
||||||
|
mstore(add(mIn, 160), calldataload(pEval_s2)) |
||||||
|
mstore(add(mIn, 192), calldataload(pEval_zw)) |
||||||
|
|
||||||
|
let v1 := mod(keccak256(mIn, 224), q) |
||||||
|
mstore(add(pMem, pV1), v1) |
||||||
|
|
||||||
|
// challenges.beta * challenges.xi |
||||||
|
mstore(add(pMem, pBetaXi), mulmod(beta, aux, q)) |
||||||
|
|
||||||
|
// challenges.xi^n |
||||||
|
<%for (let i=0; i<power;i++) {%> |
||||||
|
aux:= mulmod(aux, aux, q) |
||||||
|
<%}%> |
||||||
|
mstore(add(pMem, pXin), aux) |
||||||
|
|
||||||
|
// Zh |
||||||
|
aux:= mod(add(sub(aux, 1), q), q) |
||||||
|
mstore(add(pMem, pZh), aux) |
||||||
|
mstore(add(pMem, pZhInv), aux) // We will invert later together with lagrange pols |
||||||
|
|
||||||
|
// challenges.v^2, challenges.v^3, challenges.v^4, challenges.v^5 |
||||||
|
aux := mulmod(v1, v1, q) |
||||||
|
mstore(add(pMem, pV2), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV3), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV4), aux) |
||||||
|
aux := mulmod(aux, v1, q) |
||||||
|
mstore(add(pMem, pV5), aux) |
||||||
|
|
||||||
|
// challenges.u |
||||||
|
mstore(mIn, calldataload(pWxi)) |
||||||
|
mstore(add(mIn, 32), calldataload(add(pWxi, 32))) |
||||||
|
mstore(add(mIn, 64), calldataload(pWxiw)) |
||||||
|
mstore(add(mIn, 96), calldataload(add(pWxiw, 32))) |
||||||
|
|
||||||
|
mstore(add(pMem, pU), mod(keccak256(mIn, 128), q)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateLagrange(pMem) { |
||||||
|
let w := 1 |
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l<%=i%>), |
||||||
|
mulmod( |
||||||
|
n, |
||||||
|
mod( |
||||||
|
add( |
||||||
|
sub( |
||||||
|
mload(add(pMem, pXi)), |
||||||
|
w |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% if (i<Math.max(nPublic, 1)) { %> |
||||||
|
w := mulmod(w, w1, q) |
||||||
|
<% } %> |
||||||
|
<% } %> |
||||||
|
|
||||||
|
inverseArray(add(pMem, pZhInv), <%=Math.max(nPublic, 1)+1%> ) |
||||||
|
|
||||||
|
let zh := mload(add(pMem, pZh)) |
||||||
|
w := 1 |
||||||
|
<% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> |
||||||
|
<% if (i==1) { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l1 ), |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l1 )), |
||||||
|
zh, |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% } else { %> |
||||||
|
mstore( |
||||||
|
add(pMem, pEval_l<%=i%>), |
||||||
|
mulmod( |
||||||
|
w, |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l<%=i%>)), |
||||||
|
zh, |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
) |
||||||
|
<% } %> |
||||||
|
<% if (i<Math.max(nPublic, 1)) { %> |
||||||
|
w := mulmod(w, w1, q) |
||||||
|
<% } %> |
||||||
|
<% } %> |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function calculatePI(pMem, pPub) { |
||||||
|
let pl := 0 |
||||||
|
|
||||||
|
<% for (let i=0; i<nPublic; i++) { %> |
||||||
|
pl := mod( |
||||||
|
add( |
||||||
|
sub( |
||||||
|
pl, |
||||||
|
mulmod( |
||||||
|
mload(add(pMem, pEval_l<%=i+1%>)), |
||||||
|
calldataload(add(pPub, <%=i*32%>)), |
||||||
|
q |
||||||
|
) |
||||||
|
), |
||||||
|
q |
||||||
|
), |
||||||
|
q |
||||||
|
) |
||||||
|
<% } %> |
||||||
|
|
||||||
|
mstore(add(pMem, pPI), pl) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateR0(pMem) { |
||||||
|
let e1 := mload(add(pMem, pPI)) |
||||||
|
|
||||||
|
let e2 := mulmod(mload(add(pMem, pEval_l1)), mload(add(pMem, pAlpha2)), q) |
||||||
|
|
||||||
|
let e3a := addmod( |
||||||
|
calldataload(pEval_a), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), |
||||||
|
q) |
||||||
|
e3a := addmod(e3a, mload(add(pMem, pGamma)), q) |
||||||
|
|
||||||
|
let e3b := addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), |
||||||
|
q) |
||||||
|
e3b := addmod(e3b, mload(add(pMem, pGamma)), q) |
||||||
|
|
||||||
|
let e3c := addmod( |
||||||
|
calldataload(pEval_c), |
||||||
|
mload(add(pMem, pGamma)), |
||||||
|
q) |
||||||
|
|
||||||
|
let e3 := mulmod(mulmod(e3a, e3b, q), e3c, q) |
||||||
|
e3 := mulmod(e3, calldataload(pEval_zw), q) |
||||||
|
e3 := mulmod(e3, mload(add(pMem, pAlpha)), q) |
||||||
|
|
||||||
|
let r0 := addmod(e1, mod(sub(q, e2), q), q) |
||||||
|
r0 := addmod(r0, mod(sub(q, e3), q), q) |
||||||
|
|
||||||
|
mstore(add(pMem, pEval_r0) , r0) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_set(pR, pP) { |
||||||
|
mstore(pR, mload(pP)) |
||||||
|
mstore(add(pR, 32), mload(add(pP,32))) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_setC(pR, x, y) { |
||||||
|
mstore(pR, x) |
||||||
|
mstore(add(pR, 32), y) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_calldataSet(pR, pP) { |
||||||
|
mstore(pR, calldataload(pP)) |
||||||
|
mstore(add(pR, 32), calldataload(add(pP, 32))) |
||||||
|
} |
||||||
|
|
||||||
|
function g1_acc(pR, pP) { |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, mload(pR)) |
||||||
|
mstore(add(mIn,32), mload(add(pR, 32))) |
||||||
|
mstore(add(mIn,64), mload(pP)) |
||||||
|
mstore(add(mIn,96), mload(add(pP, 32))) |
||||||
|
|
||||||
|
let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulAcc(pR, pP, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, mload(pP)) |
||||||
|
mstore(add(mIn,32), mload(add(pP, 32))) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
mstore(add(mIn,64), mload(pR)) |
||||||
|
mstore(add(mIn,96), mload(add(pR, 32))) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulAccC(pR, x, y, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, x) |
||||||
|
mstore(add(mIn,32), y) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
mstore(add(mIn,64), mload(pR)) |
||||||
|
mstore(add(mIn,96), mload(add(pR, 32))) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulSetC(pR, x, y, s) { |
||||||
|
let success |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(mIn, x) |
||||||
|
mstore(add(mIn,32), y) |
||||||
|
mstore(add(mIn,64), s) |
||||||
|
|
||||||
|
success := staticcall(sub(gas(), 2000), 7, mIn, 96, pR, 64) |
||||||
|
|
||||||
|
if iszero(success) { |
||||||
|
mstore(0, 0) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function g1_mulSet(pR, pP, s) { |
||||||
|
g1_mulSetC(pR, mload(pP), mload(add(pP, 32)), s) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateD(pMem) { |
||||||
|
let _pD:= add(pMem, pD) |
||||||
|
let gamma := mload(add(pMem, pGamma)) |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(0x40, add(mIn, 256)) // d1, d2, d3 & d4 (4*64 bytes) |
||||||
|
|
||||||
|
g1_setC(_pD, Qcx, Qcy) |
||||||
|
g1_mulAccC(_pD, Qmx, Qmy, mulmod(calldataload(pEval_a), calldataload(pEval_b), q)) |
||||||
|
g1_mulAccC(_pD, Qlx, Qly, calldataload(pEval_a)) |
||||||
|
g1_mulAccC(_pD, Qrx, Qry, calldataload(pEval_b)) |
||||||
|
g1_mulAccC(_pD, Qox, Qoy, calldataload(pEval_c)) |
||||||
|
|
||||||
|
let betaxi := mload(add(pMem, pBetaXi)) |
||||||
|
let val1 := addmod( |
||||||
|
addmod(calldataload(pEval_a), betaxi, q), |
||||||
|
gamma, q) |
||||||
|
|
||||||
|
let val2 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(betaxi, k1, q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
let val3 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_c), |
||||||
|
mulmod(betaxi, k2, q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
let d2a := mulmod( |
||||||
|
mulmod(mulmod(val1, val2, q), val3, q), |
||||||
|
mload(add(pMem, pAlpha)), |
||||||
|
q |
||||||
|
) |
||||||
|
|
||||||
|
let d2b := mulmod( |
||||||
|
mload(add(pMem, pEval_l1)), |
||||||
|
mload(add(pMem, pAlpha2)), |
||||||
|
q |
||||||
|
) |
||||||
|
|
||||||
|
// We'll use mIn to save d2 |
||||||
|
g1_calldataSet(add(mIn, 192), pZ) |
||||||
|
g1_mulSet( |
||||||
|
mIn, |
||||||
|
add(mIn, 192), |
||||||
|
addmod(addmod(d2a, d2b, q), mload(add(pMem, pU)), q)) |
||||||
|
|
||||||
|
|
||||||
|
val1 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_a), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
val2 := addmod( |
||||||
|
addmod( |
||||||
|
calldataload(pEval_b), |
||||||
|
mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), |
||||||
|
q), gamma, q) |
||||||
|
|
||||||
|
val3 := mulmod( |
||||||
|
mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pBeta)), q), |
||||||
|
calldataload(pEval_zw), q) |
||||||
|
|
||||||
|
|
||||||
|
// We'll use mIn + 64 to save d3 |
||||||
|
g1_mulSetC( |
||||||
|
add(mIn, 64), |
||||||
|
S3x, |
||||||
|
S3y, |
||||||
|
mulmod(mulmod(val1, val2, q), val3, q)) |
||||||
|
|
||||||
|
// We'll use mIn + 128 to save d4 |
||||||
|
g1_calldataSet(add(mIn, 128), pT1) |
||||||
|
|
||||||
|
g1_mulAccC(add(mIn, 128), calldataload(pT2), calldataload(add(pT2, 32)), mload(add(pMem, pXin))) |
||||||
|
let xin2 := mulmod(mload(add(pMem, pXin)), mload(add(pMem, pXin)), q) |
||||||
|
g1_mulAccC(add(mIn, 128), calldataload(pT3), calldataload(add(pT3, 32)) , xin2) |
||||||
|
|
||||||
|
g1_mulSetC(add(mIn, 128), mload(add(mIn, 128)), mload(add(mIn, 160)), mload(add(pMem, pZh))) |
||||||
|
|
||||||
|
mstore(add(add(mIn, 64), 32), mod(sub(qf, mload(add(add(mIn, 64), 32))), qf)) |
||||||
|
mstore(add(mIn, 160), mod(sub(qf, mload(add(mIn, 160))), qf)) |
||||||
|
g1_acc(_pD, mIn) |
||||||
|
g1_acc(_pD, add(mIn, 64)) |
||||||
|
g1_acc(_pD, add(mIn, 128)) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateF(pMem) { |
||||||
|
let p := add(pMem, pF) |
||||||
|
|
||||||
|
g1_set(p, add(pMem, pD)) |
||||||
|
g1_mulAccC(p, calldataload(pA), calldataload(add(pA, 32)), mload(add(pMem, pV1))) |
||||||
|
g1_mulAccC(p, calldataload(pB), calldataload(add(pB, 32)), mload(add(pMem, pV2))) |
||||||
|
g1_mulAccC(p, calldataload(pC), calldataload(add(pC, 32)), mload(add(pMem, pV3))) |
||||||
|
g1_mulAccC(p, S1x, S1y, mload(add(pMem, pV4))) |
||||||
|
g1_mulAccC(p, S2x, S2y, mload(add(pMem, pV5))) |
||||||
|
} |
||||||
|
|
||||||
|
function calculateE(pMem) { |
||||||
|
let s := mod(sub(q, mload(add(pMem, pEval_r0))), q) |
||||||
|
|
||||||
|
s := addmod(s, mulmod(calldataload(pEval_a), mload(add(pMem, pV1)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_b), mload(add(pMem, pV2)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_c), mload(add(pMem, pV3)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_s1), mload(add(pMem, pV4)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_s2), mload(add(pMem, pV5)), q), q) |
||||||
|
s := addmod(s, mulmod(calldataload(pEval_zw), mload(add(pMem, pU)), q), q) |
||||||
|
|
||||||
|
g1_mulSetC(add(pMem, pE), G1x, G1y, s) |
||||||
|
} |
||||||
|
|
||||||
|
function checkPairing(pMem) -> isOk { |
||||||
|
let mIn := mload(0x40) |
||||||
|
mstore(0x40, add(mIn, 576)) // [0..383] = pairing data, [384..447] = pWxi, [448..512] = pWxiw |
||||||
|
|
||||||
|
let _pWxi := add(mIn, 384) |
||||||
|
let _pWxiw := add(mIn, 448) |
||||||
|
let _aux := add(mIn, 512) |
||||||
|
|
||||||
|
g1_calldataSet(_pWxi, pWxi) |
||||||
|
g1_calldataSet(_pWxiw, pWxiw) |
||||||
|
|
||||||
|
// A1 |
||||||
|
g1_mulSet(mIn, _pWxiw, mload(add(pMem, pU))) |
||||||
|
g1_acc(mIn, _pWxi) |
||||||
|
mstore(add(mIn, 32), mod(sub(qf, mload(add(mIn, 32))), qf)) |
||||||
|
|
||||||
|
// [X]_2 |
||||||
|
mstore(add(mIn,64), X2x2) |
||||||
|
mstore(add(mIn,96), X2x1) |
||||||
|
mstore(add(mIn,128), X2y2) |
||||||
|
mstore(add(mIn,160), X2y1) |
||||||
|
|
||||||
|
// B1 |
||||||
|
g1_mulSet(add(mIn, 192), _pWxi, mload(add(pMem, pXi))) |
||||||
|
|
||||||
|
let s := mulmod(mload(add(pMem, pU)), mload(add(pMem, pXi)), q) |
||||||
|
s := mulmod(s, w1, q) |
||||||
|
g1_mulSet(_aux, _pWxiw, s) |
||||||
|
g1_acc(add(mIn, 192), _aux) |
||||||
|
g1_acc(add(mIn, 192), add(pMem, pF)) |
||||||
|
mstore(add(pMem, add(pE, 32)), mod(sub(qf, mload(add(pMem, add(pE, 32)))), qf)) |
||||||
|
g1_acc(add(mIn, 192), add(pMem, pE)) |
||||||
|
|
||||||
|
// [1]_2 |
||||||
|
mstore(add(mIn,256), G2x2) |
||||||
|
mstore(add(mIn,288), G2x1) |
||||||
|
mstore(add(mIn,320), G2y2) |
||||||
|
mstore(add(mIn,352), G2y1) |
||||||
|
|
||||||
|
let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20) |
||||||
|
|
||||||
|
isOk := and(success, mload(mIn)) |
||||||
|
} |
||||||
|
|
||||||
|
let pMem := mload(0x40) |
||||||
|
mstore(0x40, add(pMem, lastMem)) |
||||||
|
|
||||||
|
checkInput() |
||||||
|
calculateChallenges(pMem, _pubSignals) |
||||||
|
calculateLagrange(pMem) |
||||||
|
calculatePI(pMem, _pubSignals) |
||||||
|
calculateR0(pMem) |
||||||
|
calculateD(pMem) |
||||||
|
calculateF(pMem) |
||||||
|
calculateE(pMem) |
||||||
|
let isValid := checkPairing(pMem) |
||||||
|
|
||||||
|
mstore(0x40, sub(pMem, lastMem)) |
||||||
|
mstore(0, isValid) |
||||||
|
return(0,0x20) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -1,10 +1,10 @@ |
|||||||
{ |
{ |
||||||
"version": "v0.46.0", |
"version": "v0.47.0", |
||||||
"title": "RELEASE HIGHLIGHTS", |
"title": "RELEASE HIGHLIGHTS", |
||||||
"highlight1": "Remix VM (Cancun) added with the functionalities of cancun fork", |
"highlight1": "Contract pinning enabled for Remix VMs too", |
||||||
"highlight2": "Latest Solidity compiler version set to 0.8.25 with cancun as default EVM version", |
"highlight2": "Dapp draft plugin added to edit & deploy a DApp", |
||||||
"highlight3": "", |
"highlight3": "AI tools integrated using Solcoder", |
||||||
"highlight4": "", |
"highlight4": "", |
||||||
"more": "Read More", |
"more": "Read More", |
||||||
"moreLink": "https://medium.com/remix-ide/remix-release-v0-46-0-f90a4948fff0" |
"moreLink": "https://medium.com/remix-ide/remix-release-v0-47-0-d52f3dc21886" |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue