Transaction Hash:
Block:
18397568 at Oct-21-2023 08:43:11 AM +UTC
Transaction Fee:
0.009254932017428922 ETH
$21.75
Gas Used:
768,662 Gas / 12.040314231 Gwei
Emitted Events:
178 |
DiamondProxy.0x22c9005dd88c18b552a1cd7e8b3b937fcde9ca69213c1f658f54d572e4877a81( 0x22c9005dd88c18b552a1cd7e8b3b937fcde9ca69213c1f658f54d572e4877a81, 0x0000000000000000000000000000000000000000000000000000000000044202, 0x0000000000000000000000000000000000000000000000000000000000044203 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x32400084...60a000324 | (zkSync Era: Diamond Proxy) | ||||
0x35274399...f6e572092 | (zkSync Era: Validator) |
62.827315380222759467 Eth
Nonce: 409876
|
62.818060448205330545 Eth
Nonce: 409877
| 0.009254932017428922 | |
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 16.571063152475549202 Eth | 16.573850503489356598 Eth | 0.002787351013807396 |
Execution Trace
ValidatorTimelock.proveBlocks( [{name:blockNumber, type:uint64, order:1, indexed:false, value:279042, valueString:279042}, {name:blockHash, type:bytes32, order:2, indexed:false, value:07F65002FC74857891B8E019E044B0B7089331A3D28EEE8E3EBE75692928DCE6, valueString:07F65002FC74857891B8E019E044B0B7089331A3D28EEE8E3EBE75692928DCE6}, {name:indexRepeatedStorageChanges, type:uint64, order:3, indexed:false, value:155112084, valueString:155112084}, {name:numberOfLayer1Txs, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:priorityOperationsHash, type:bytes32, order:5, indexed:false, value:C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470, valueString:C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470}, {name:l2LogsTreeRoot, type:bytes32, order:6, indexed:false, value:15067CF913DAC1615BFF96140539ED4EC32653AC5F7371D128E4C28221B99EE2, valueString:15067CF913DAC1615BFF96140539ED4EC32653AC5F7371D128E4C28221B99EE2}, {name:timestamp, type:uint256, order:7, indexed:false, value:1697864134, valueString:1697864134}, {name:commitment, type:bytes32, order:8, indexed:false, value:59C59A8F3F1EC7EFEB5AC1BC5FB74B8DA022AD7CB48ACD7D98289D432EA5665D, valueString:59C59A8F3F1EC7EFEB5AC1BC5FB74B8DA022AD7CB48ACD7D98289D432EA5665D}], , [{name:recursiveAggregationInput, type:uint256[], order:1, indexed:false, value:[12479614162417257985851635572175346371038518426734902444938177749025134622934, 12096419890896732156837789287282633943233949603272065734671105920393686962763, 5480457859896407437399382774047062565040861847341240340750236886848786943745, 12482417594709984840088718898048881187195438390152718636147819310226720234833], valueString:[12479614162417257985851635572175346371038518426734902444938177749025134622934, 12096419890896732156837789287282633943233949603272065734671105920393686962763, 5480457859896407437399382774047062565040861847341240340750236886848786943745, 12482417594709984840088718898048881187195438390152718636147819310226720234833]}, {name:serializedProof, type:uint256[], order:2, indexed:false, value:[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477], valueString:[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477]}] )
DiamondProxy.7739cbe7( )
ExecutorFacet.proveBlocks( _prevBlock=[{name:blockNumber, type:uint64, order:1, indexed:false, value:279042, valueString:279042}, {name:blockHash, type:bytes32, order:2, indexed:false, value:07F65002FC74857891B8E019E044B0B7089331A3D28EEE8E3EBE75692928DCE6, valueString:07F65002FC74857891B8E019E044B0B7089331A3D28EEE8E3EBE75692928DCE6}, {name:indexRepeatedStorageChanges, type:uint64, order:3, indexed:false, value:155112084, valueString:155112084}, {name:numberOfLayer1Txs, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:priorityOperationsHash, type:bytes32, order:5, indexed:false, value:C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470, valueString:C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470}, {name:l2LogsTreeRoot, type:bytes32, order:6, indexed:false, value:15067CF913DAC1615BFF96140539ED4EC32653AC5F7371D128E4C28221B99EE2, valueString:15067CF913DAC1615BFF96140539ED4EC32653AC5F7371D128E4C28221B99EE2}, {name:timestamp, type:uint256, order:7, indexed:false, value:1697864134, valueString:1697864134}, {name:commitment, type:bytes32, order:8, indexed:false, value:59C59A8F3F1EC7EFEB5AC1BC5FB74B8DA022AD7CB48ACD7D98289D432EA5665D, valueString:59C59A8F3F1EC7EFEB5AC1BC5FB74B8DA022AD7CB48ACD7D98289D432EA5665D}], _committedBlocks=, _proof=[{name:recursiveAggregationInput, type:uint256[], order:1, indexed:false, value:[12479614162417257985851635572175346371038518426734902444938177749025134622934, 12096419890896732156837789287282633943233949603272065734671105920393686962763, 5480457859896407437399382774047062565040861847341240340750236886848786943745, 12482417594709984840088718898048881187195438390152718636147819310226720234833], valueString:[12479614162417257985851635572175346371038518426734902444938177749025134622934, 12096419890896732156837789287282633943233949603272065734671105920393686962763, 5480457859896407437399382774047062565040861847341240340750236886848786943745, 12482417594709984840088718898048881187195438390152718636147819310226720234833]}, {name:serializedProof, type:uint256[], order:2, indexed:false, value:[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477], valueString:[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477]}] )
Verifier.verify_serialized_proof( public_inputs=[381921557989928181181384233298289158277820569422286091151545044311181834105], serialized_proof=[3524113225187557177145107617838243498761999794688773258567841219968474944954, 939551232131613154661018747205459184284655724982920210389434365341124809780, 14592110089722630491961621824782919951329593031799140705229425209943073075549, 20278769494517606138123959528992648230137537791133184638712127086928084984616, 7310013787799985289147909400201887668247230647752670858991470418550271450295, 4919757195352184136891275128696579687293756966737655522879227619492211352106, 17744583829609829053793205426901580446689096150296564272881753381436212147307, 15183428383958677632027129163905716880179569624687908214414719877581545085642, 8444271483854449342149401553674881252311210816395951967526068846575761805882, 1393673823636356316061027747152241781928085734505807252600313345019164204103, 7167417020919022673129109052877669255293429425405176289020472038939704706745, 5824317826819809260596892590523791713084956014335117895770471046758226718951, 12418816712753585483392748407264974608852210637370240800255574992991031341405, 118238131484629200443400306917527326841421988370181312822524736698064653344, 10666304398177637647890753504334521737151201223095213134254312224092629313074, 19047196432631033527354713662585366457825136016474476889084600279623824446911, 14786205737159662766675095278656402461870821682222961329410153011675065462978, 2586278952810363142284132890844008247447283631373686296075463150728359906382, 19139872302774805458293790107205853728663321619804201616013295912998592257548, 11980333203636680912051841673591401000198176293503096560238745401123853682312, 16324188105541805716341826978005136908604020512353953342150852095915760513194, 7470752778924287779870315373556130859196624810270986046224420331146138947897, 11223631808228726277386301241893312794476826293864884270667314929558328413013, 21822281317575282084854144547943539937423963501669755895680283940582570284872, 13460167685703842367022761622677530223541188753335727022447565663054790657959, 1201259195529594702328961343661970568675189008318071825797664469171825133542, 435455904917054716867479869471704368618951986998397435853598218621926177434, 19068193777812264824799448654372901315470637592538576616914813567061680089960, 21379247076919355403113513485946504024256953126347538021795483874383844094902, 13637303803190422042048869685283935942532430910617658897123410892197563422691, 18388422462962312195249556491427734585040463185799028538596526017769107504031, 16129919118862721242705182029390173663414011132489952154786177404790523936152, 453463711523946109659592323099138507032915272515308821035988612316322454085, 16474681642205798179741312412599072500844775888546419043841522020796985320129, 7925924055696577196837400058350129808764172501729331899682904387234617047684, 10530329938865876402207219949450860534726224155618152285400024858305435632327, 11181512381702728565294766539037085731621618621896208303947816502691943570307, 1394305928411987385617751465948380043804833794573070452361590715046721551796, 10694528246383965736309647664312405267564368334162861757929476020267175122674, 12526163083547819470328901889601643011695790950956244657517512126954858866655, 6204884064671025925435896405957598540759837949118266255778660503956449894999, 17777262403382895294093110659495717464323470440637449901955031309665574470058, 20577078467734291679941510612966154436842174642116537363727871191948175894974, 8275974482397456933815284516428572229381209744913006549271654685826331497477] ) => ( True )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...007.20b0b224( )
-
Null: 0x000...006.1794ea12( )
-
Null: 0x000...007.2a50c8a8( )
-
Null: 0x000...006.0d6089f6( )
-
Null: 0x000...007.241729bd( )
-
Null: 0x000...006.15fc6ddd( )
-
Null: 0x000...007.1b7d54f8( )
-
Null: 0x000...006.0ebe0de4( )
-
Null: 0x000...007.0138733c( )
-
Null: 0x000...006.0c8f6a9f( )
-
Null: 0x000...007.1b1be727( )
-
Null: 0x000...006.122d63da( )
-
Null: 0x000...007.14c289d7( )
-
Null: 0x000...007.210b5cc8( )
-
Null: 0x000...006.2174774b( )
-
Null: 0x000...007.29d4d14a( )
-
Null: 0x000...006.04c46cdf( )
-
Null: 0x000...007.06d15382( )
-
Null: 0x000...006.2ffcbb86( )
-
Null: 0x000...007.274a668d( )
-
Null: 0x000...006.152af99b( )
-
Null: 0x000...007.0ba53bf4( )
-
Null: 0x000...006.14bc1172( )
-
Null: 0x000...006.0dbd4fb5( )
-
Null: 0x000...007.09259595( )
-
Null: 0x000...006.1ce29553( )
-
Null: 0x000...007.19028eb9( )
-
Null: 0x000...007.143471a1( )
-
Null: 0x000...006.2622b31c( )
-
Null: 0x000...007.12ab49cd( )
-
Null: 0x000...006.00fde849( )
-
Null: 0x000...007.2f3ede68( )
-
Null: 0x000...006.0596bd67( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...005.00000000( )
-
Null: 0x000...007.12ab49cd( )
-
Null: 0x000...006.1381e59a( )
-
Null: 0x000...007.0fd89d40( )
-
Null: 0x000...007.1b74cd19( )
-
Null: 0x000...006.1f4a6877( )
-
Null: 0x000...006.0df5a253( )
-
Null: 0x000...007.2512bbc0( )
-
Null: 0x000...006.20177830( )
-
Null: 0x000...007.07ca93d0( )
-
Null: 0x000...006.2427610f( )
-
Null: 0x000...007.2042d774( )
-
Null: 0x000...006.288384b4( )
-
Null: 0x000...007.10295239( )
-
Null: 0x000...006.1337e898( )
-
Null: 0x000...007.273b141a( )
-
Null: 0x000...006.010feec4( )
-
Null: 0x000...007.28f2a0a9( )
-
Null: 0x000...006.2f87dede( )
-
Null: 0x000...007.1423fa82( )
-
Null: 0x000...006.17be9526( )
-
Null: 0x000...007.0e9b5b12( )
-
Null: 0x000...006.2fd213d8( )
-
Null: 0x000...007.25d1a714( )
-
Null: 0x000...006.2a6e1648( )
-
Null: 0x000...007.201a04b4( )
-
Null: 0x000...006.0f89d993( )
-
Null: 0x000...007.1f814e2d( )
-
Null: 0x000...006.1f3659e9( )
-
Null: 0x000...007.2f85df2d( )
-
Null: 0x000...006.05e9e800( )
-
Null: 0x000...007.12ab49cd( )
-
Null: 0x000...007.273b141a( )
-
Null: 0x000...006.2d85f214( )
-
Null: 0x000...007.0fd89d40( )
-
Null: 0x000...006.14d4d84f( )
-
Null: 0x000...007.1b74cd19( )
-
Null: 0x000...006.14e0bb3f( )
-
Null: 0x000...007.201a04b4( )
-
Null: 0x000...006.13d6f949( )
-
Null: 0x000...007.00000000( )
-
Null: 0x000...007.1efc9e34( )
-
Null: 0x000...006.08fe87db( )
-
Null: 0x000...007.00000000( )
-
Null: 0x000...006.1e4d84a1( )
-
Null: 0x000...007.0db7d705( )
-
Null: 0x000...007.2d7e36b4( )
-
Null: 0x000...006.2dbb4032( )
-
Null: 0x000...006.1d11ff0a( )
-
Null: 0x000...007.2d7e36b4( )
-
Null: 0x000...006.2e62903e( )
-
Null: 0x000...008.1050904d( )
-
-
Null: 0x000...008.1b973618( )
proveBlocks[ValidatorTimelock (ln:285)]
_propagateToZkSync[ValidatorTimelock (ln:290)]
File 1 of 4: ValidatorTimelock
File 2 of 4: DiamondProxy
File 3 of 4: ExecutorFacet
File 4 of 4: Verifier
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol) pragma solidity ^0.8.0; import "./Ownable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2Step is Ownable { address private _pendingOwner; event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual override onlyOwner { _pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete _pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() external { address sender = _msgSender(); require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner"); _transferOwnership(sender); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT interface IExecutor { /// @notice Rollup block stored data /// @param blockNumber Rollup block number /// @param blockHash Hash of L2 block /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more /// @param numberOfLayer1Txs Number of priority operations to be processed /// @param priorityOperationsHash Hash of all priority operations from this block /// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this block /// @param timestamp Rollup block timestamp, have the same format as Ethereum block constant /// @param commitment Verified input for the zkSync circuit struct StoredBlockInfo { uint64 blockNumber; bytes32 blockHash; uint64 indexRepeatedStorageChanges; uint256 numberOfLayer1Txs; bytes32 priorityOperationsHash; bytes32 l2LogsTreeRoot; uint256 timestamp; bytes32 commitment; } /// @notice Data needed to commit new block /// @param blockNumber Number of the committed block /// @param timestamp Unix timestamp denoting the start of the block execution /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more /// @param newStateRoot The state root of the full state tree /// @param numberOfLayer1Txs Number of priority operations to be processed /// @param l2LogsTreeRoot The root hash of the tree that contains all L2 -> L1 logs in the block /// @param priorityOperationsHash Hash of all priority operations from this block /// @param initialStorageChanges Storage write access as a concatenation key-value /// @param repeatedStorageChanges Storage write access as a concatenation index-value /// @param l2Logs concatenation of all L2 -> L1 logs in the block /// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract /// @param factoryDeps array of l2 bytecodes that were marked as known on L2 struct CommitBlockInfo { uint64 blockNumber; uint64 timestamp; uint64 indexRepeatedStorageChanges; bytes32 newStateRoot; uint256 numberOfLayer1Txs; bytes32 l2LogsTreeRoot; bytes32 priorityOperationsHash; bytes initialStorageChanges; bytes repeatedStorageChanges; bytes l2Logs; bytes[] l2ArbitraryLengthMessages; bytes[] factoryDeps; } /// @notice Recursive proof input data (individual commitments are constructed onchain) struct ProofInput { uint256[] recursiveAggregationInput; uint256[] serializedProof; } function commitBlocks(StoredBlockInfo calldata _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData) external; function proveBlocks( StoredBlockInfo calldata _prevBlock, StoredBlockInfo[] calldata _committedBlocks, ProofInput calldata _proof ) external; function executeBlocks(StoredBlockInfo[] calldata _blocksData) external; function revertBlocks(uint256 _newLastBlock) external; /// @notice Event emitted when a block is committed event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment); /// @notice Event emitted when blocks are verified event BlocksVerification(uint256 indexed previousLastVerifiedBlock, uint256 indexed currentLastVerifiedBlock); /// @notice Event emitted when a block is executed event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment); /// @notice Event emitted when blocks are reverted event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted); } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "./interfaces/IExecutor.sol"; /// @author Matter Labs /// @notice Intermediate smart contract between the validator EOA account and the zkSync smart contract. /// @dev The primary purpose of this contract is to provide a trustless means of delaying block execution without /// modifying the main zkSync contract. As such, even if this contract is compromised, it will not impact the main contract. /// @dev zkSync actively monitors the chain activity and reacts to any suspicious activity by freezing the chain. /// This allows time for investigation and mitigation before resuming normal operations. /// @dev The contract overloads all of the 4 methods, that are used in state transition. When the block is committed, the /// timestamp is stored for it. Later, when the owner calls the block execution, the contract checks that block /// was committed not earlier than X time ago. contract ValidatorTimelock is IExecutor, Ownable2Step { /// @notice The delay between committing and executing blocks is changed. event NewExecutionDelay(uint256 _newExecutionDelay); /// @notice The validator address is changed. event NewValidator(address _oldValidator, address _newValidator); /// @dev The main zkSync smart contract. address public immutable zkSyncContract; /// @dev The mapping of L2 block number => timestamp when it was commited. mapping(uint256 => uint256) public committedBlockTimestamp; /// @dev The address that can commit/revert/validate/execute blocks. address public validator; /// @dev The delay between committing and executing blocks. uint256 public executionDelay; constructor( address _initialOwner, address _zkSyncContract, uint256 _executionDelay, address _validator ) { _transferOwnership(_initialOwner); zkSyncContract = _zkSyncContract; executionDelay = _executionDelay; validator = _validator; } /// @dev Set new validator address. function setValidator(address _newValidator) external onlyOwner { address oldValidator = validator; validator = _newValidator; emit NewValidator(oldValidator, _newValidator); } /// @dev Set the delay between committing and executing blocks. function setExecutionDelay(uint256 _executionDelay) external onlyOwner { executionDelay = _executionDelay; emit NewExecutionDelay(_executionDelay); } /// @notice Checks if the caller is a validator. modifier onlyValidator() { require(msg.sender == validator, "8h"); _; } /// @dev Records the timestamp for all provided committed blocks and make /// a call to the zkSync contract with the same calldata. function commitBlocks(StoredBlockInfo calldata, CommitBlockInfo[] calldata _newBlocksData) external onlyValidator { for (uint256 i = 0; i < _newBlocksData.length; ++i) { committedBlockTimestamp[_newBlocksData[i].blockNumber] = block.timestamp; } _propagateToZkSync(); } /// @dev Make a call to the zkSync contract with the same calldata. /// Note: If the block is reverted, it needs to be committed first before the execution. /// So it's safe to not override the committed blocks. function revertBlocks(uint256) external onlyValidator { _propagateToZkSync(); } /// @dev Make a call to the zkSync contract with the same calldata. /// Note: We don't track the time when blocks are proven, since all information about /// the block is known on the commit stage and the proved is not finalized (may be reverted). function proveBlocks( StoredBlockInfo calldata, StoredBlockInfo[] calldata, ProofInput calldata ) external onlyValidator { _propagateToZkSync(); } /// @dev Check that blocks were committed at least X time ago and /// make a call to the zkSync contract with the same calldata. function executeBlocks(StoredBlockInfo[] calldata _newBlocksData) external onlyValidator { for (uint256 i = 0; i < _newBlocksData.length; ++i) { uint256 commitBlockTimestamp = committedBlockTimestamp[_newBlocksData[i].blockNumber]; // Note: if the `commitBlockTimestamp` is zero, that means either: // * The block was committed, but not though this contract. // * The block wasn't committed at all, so execution will fail in the zkSync contract. // We allow executing such blocks. require(block.timestamp > commitBlockTimestamp + executionDelay, "5c"); // The delay is not passed } _propagateToZkSync(); } /// @dev Call the zkSync contract with the same calldata as this contract was called. /// Note: it is called the zkSync contract, not delegatecalled! function _propagateToZkSync() internal { address contractAddress = zkSyncContract; assembly { // Copy function signature and arguments from calldata at zero position into memory at pointer position calldatacopy(0, 0, calldatasize()) // Call method of the zkSync contract returns 0 on error let result := call(gas(), contractAddress, 0, 0, calldatasize(), 0, 0) // Get the size of the last return data let size := returndatasize() // Copy the size length of bytes from return data at zero position to pointer position returndatacopy(0, 0, size) // Depending on the result value switch result case 0 { // End execution and revert state changes revert(0, size) } default { // Return data with length of size at pointers position return(0, size) } } } }
File 2 of 4: DiamondProxy
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } } pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT library UncheckedMath { function uncheckedInc(uint256 _number) internal pure returns (uint256) { unchecked { return _number + 1; } } function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) { unchecked { return _lhs + _rhs; } } } pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT import "./libraries/Diamond.sol"; /// @title Diamond Proxy Contract (EIP-2535) /// @author Matter Labs contract DiamondProxy { constructor(uint256 _chainId, Diamond.DiamondCutData memory _diamondCut) { // Check that the contract is deployed on the expected chain. // Thus, the contract deployed by the same Create2 factory on the different chain will have different addresses! require(_chainId == block.chainid, "pr"); Diamond.diamondCut(_diamondCut); } /// @dev 1. Find the facet for the function that is called. /// @dev 2. Delegate the execution to the found facet via `delegatecall`. fallback() external payable { Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage(); // Check whether the data contains a "full" selector or it is empty. // Required because Diamond proxy finds a facet by function signature, // which is not defined for data length in range [1, 3]. require(msg.data.length >= 4 || msg.data.length == 0, "Ut"); // Get facet from function selector Diamond.SelectorToFacet memory facet = diamondStorage.selectorToFacet[msg.sig]; address facetAddress = facet.facetAddress; require(facetAddress != address(0), "F"); // Proxy has no facet for this selector require(!diamondStorage.isFrozen || !facet.isFreezable, "q1"); // Facet is frozen assembly { // The pointer to the free memory slot let ptr := mload(0x40) // Copy function signature and arguments from calldata at zero position into memory at pointer position calldatacopy(ptr, 0, calldatasize()) // Delegatecall method of the implementation contract returns 0 on error let result := delegatecall(gas(), facetAddress, ptr, calldatasize(), 0, 0) // Get the size of the last return data let size := returndatasize() // Copy the size length of bytes from return data at zero position to pointer position returndatacopy(ptr, 0, size) // Depending on the result value switch result case 0 { // End execution and revert state changes revert(ptr, size) } default { // Return data with length of size at pointers position return(ptr, size) } } } } pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "../../common/libraries/UncheckedMath.sol"; /// @author Matter Labs /// @notice The helper library for managing the EIP-2535 diamond proxy. library Diamond { using UncheckedMath for uint256; using SafeCast for uint256; /// @dev Magic value that should be returned by diamond cut initialize contracts. /// @dev Used to distinguish calls to contracts that were supposed to be used as diamond initializer from other contracts. bytes32 constant DIAMOND_INIT_SUCCESS_RETURN_VALUE = 0x33774e659306e47509050e97cb651e731180a42d458212294d30751925c551a2; // keccak256("diamond.zksync.init") - 1 /// @dev Storage position of `DiamondStorage` structure. bytes32 constant DIAMOND_STORAGE_POSITION = 0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b; // keccak256("diamond.standard.diamond.storage") - 1; event DiamondCut(FacetCut[] facetCuts, address initAddress, bytes initCalldata); /// @dev Utility struct that contains associated facet & meta information of selector /// @param facetAddress address of the facet which is connected with selector /// @param selectorPosition index in `FacetToSelectors.selectors` array, where is selector stored /// @param isFreezable denotes whether the selector can be frozen. struct SelectorToFacet { address facetAddress; uint16 selectorPosition; bool isFreezable; } /// @dev Utility struct that contains associated selectors & meta information of facet /// @param selectors list of all selectors that belong to the facet /// @param facetPosition index in `DiamondStorage.facets` array, where is facet stored struct FacetToSelectors { bytes4[] selectors; uint16 facetPosition; } /// @notice The structure that holds all diamond proxy associated parameters /// @dev According to the EIP-2535 should be stored on a special storage key - `DIAMOND_STORAGE_POSITION` /// @param selectorToFacet A mapping from the selector to the facet address and its meta information /// @param facetToSelectors A mapping from facet address to its selector with meta information /// @param facets The array of all unique facet addresses that belong to the diamond proxy /// @param isFrozen Denotes whether the diamond proxy is frozen and all freezable facets are not accessible struct DiamondStorage { mapping(bytes4 => SelectorToFacet) selectorToFacet; mapping(address => FacetToSelectors) facetToSelectors; address[] facets; bool isFrozen; } /// @dev Parameters for diamond changes that touch one of the facets /// @param facet The address of facet that's affected by the cut /// @param action The action that is made on the facet /// @param isFreezable Denotes whether the facet & all their selectors can be frozen /// @param selectors An array of unique selectors that belongs to the facet address struct FacetCut { address facet; Action action; bool isFreezable; bytes4[] selectors; } /// @dev Structure of the diamond proxy changes /// @param facetCuts The set of changes (adding/removing/replacement) of implementation contracts /// @param initAddress The address that's delegate called after setting up new facet changes /// @param initCalldata Calldata for the delegate call to `initAddress` struct DiamondCutData { FacetCut[] facetCuts; address initAddress; bytes initCalldata; } /// @dev Type of change over diamond: add/replace/remove facets enum Action { Add, Replace, Remove } /// @return diamondStorage The pointer to the storage where all specific diamond proxy parameters stored function getDiamondStorage() internal pure returns (DiamondStorage storage diamondStorage) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { diamondStorage.slot := position } } /// @dev Add/replace/remove any number of selectors and optionally execute a function with delegatecall /// @param _diamondCut Diamond's facet changes and the parameters to optional initialization delegatecall function diamondCut(DiamondCutData memory _diamondCut) internal { FacetCut[] memory facetCuts = _diamondCut.facetCuts; address initAddress = _diamondCut.initAddress; bytes memory initCalldata = _diamondCut.initCalldata; uint256 facetCutsLength = facetCuts.length; for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) { Action action = facetCuts[i].action; address facet = facetCuts[i].facet; bool isFacetFreezable = facetCuts[i].isFreezable; bytes4[] memory selectors = facetCuts[i].selectors; require(selectors.length > 0, "B"); // no functions for diamond cut if (action == Action.Add) { _addFunctions(facet, selectors, isFacetFreezable); } else if (action == Action.Replace) { _replaceFunctions(facet, selectors, isFacetFreezable); } else if (action == Action.Remove) { _removeFunctions(facet, selectors); } else { revert("C"); // undefined diamond cut action } } _initializeDiamondCut(initAddress, initCalldata); emit DiamondCut(facetCuts, initAddress, initCalldata); } /// @dev Add new functions to the diamond proxy /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array function _addFunctions( address _facet, bytes4[] memory _selectors, bool _isFacetFreezable ) private { DiamondStorage storage ds = getDiamondStorage(); require(_facet != address(0), "G"); // facet with zero address cannot be added // Add facet to the list of facets if the facet address is new one _saveFacetIfNew(_facet); uint256 selectorsLength = _selectors.length; for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { bytes4 selector = _selectors[i]; SelectorToFacet memory oldFacet = ds.selectorToFacet[selector]; require(oldFacet.facetAddress == address(0), "J"); // facet for this selector already exists _addOneFunction(_facet, selector, _isFacetFreezable); } } /// @dev Change associated facets to already known function selectors /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array function _replaceFunctions( address _facet, bytes4[] memory _selectors, bool _isFacetFreezable ) private { DiamondStorage storage ds = getDiamondStorage(); require(_facet != address(0), "K"); // cannot replace facet with zero address uint256 selectorsLength = _selectors.length; for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { bytes4 selector = _selectors[i]; SelectorToFacet memory oldFacet = ds.selectorToFacet[selector]; require(oldFacet.facetAddress != address(0), "L"); // it is impossible to replace the facet with zero address _removeOneFunction(oldFacet.facetAddress, selector); // Add facet to the list of facets if the facet address is a new one _saveFacetIfNew(_facet); _addOneFunction(_facet, selector, _isFacetFreezable); } } /// @dev Remove association with function and facet /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array function _removeFunctions(address _facet, bytes4[] memory _selectors) private { DiamondStorage storage ds = getDiamondStorage(); require(_facet == address(0), "a1"); // facet address must be zero uint256 selectorsLength = _selectors.length; for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) { bytes4 selector = _selectors[i]; SelectorToFacet memory oldFacet = ds.selectorToFacet[selector]; require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet _removeOneFunction(oldFacet.facetAddress, selector); } } /// @dev Add address to the list of known facets if it is not on the list yet /// NOTE: should be called ONLY before adding a new selector associated with the address function _saveFacetIfNew(address _facet) private { DiamondStorage storage ds = getDiamondStorage(); uint256 selectorsLength = ds.facetToSelectors[_facet].selectors.length; // If there are no selectors associated with facet then save facet as new one if (selectorsLength == 0) { ds.facetToSelectors[_facet].facetPosition = ds.facets.length.toUint16(); ds.facets.push(_facet); } } /// @dev Add one function to the already known facet /// NOTE: It is expected but NOT enforced that: /// - `_facet` is NON-ZERO address /// - `_facet` is already stored address in `DiamondStorage.facets` /// - `_selector` is NOT associated by another facet function _addOneFunction( address _facet, bytes4 _selector, bool _isSelectorFreezable ) private { DiamondStorage storage ds = getDiamondStorage(); uint16 selectorPosition = (ds.facetToSelectors[_facet].selectors.length).toUint16(); // if selectorPosition is nonzero, it means it is not a new facet // so the freezability of the first selector must be matched to _isSelectorFreezable // so all the selectors in a facet will have the same freezability if (selectorPosition != 0) { bytes4 selector0 = ds.facetToSelectors[_facet].selectors[0]; require(_isSelectorFreezable == ds.selectorToFacet[selector0].isFreezable, "J1"); } ds.selectorToFacet[_selector] = SelectorToFacet({ facetAddress: _facet, selectorPosition: selectorPosition, isFreezable: _isSelectorFreezable }); ds.facetToSelectors[_facet].selectors.push(_selector); } /// @dev Remove one associated function with facet /// NOTE: It is expected but NOT enforced that `_facet` is NON-ZERO address function _removeOneFunction(address _facet, bytes4 _selector) private { DiamondStorage storage ds = getDiamondStorage(); // Get index of `FacetToSelectors.selectors` of the selector and last element of array uint256 selectorPosition = ds.selectorToFacet[_selector].selectorPosition; uint256 lastSelectorPosition = ds.facetToSelectors[_facet].selectors.length - 1; // If the selector is not at the end of the array then move the last element to the selector position if (selectorPosition != lastSelectorPosition) { bytes4 lastSelector = ds.facetToSelectors[_facet].selectors[lastSelectorPosition]; ds.facetToSelectors[_facet].selectors[selectorPosition] = lastSelector; ds.selectorToFacet[lastSelector].selectorPosition = selectorPosition.toUint16(); } // Remove last element from the selectors array ds.facetToSelectors[_facet].selectors.pop(); // Finally, clean up the association with facet delete ds.selectorToFacet[_selector]; // If there are no selectors for facet then remove the facet from the list of known facets if (lastSelectorPosition == 0) { _removeFacet(_facet); } } /// @dev remove facet from the list of known facets /// NOTE: It is expected but NOT enforced that there are no selectors associated with `_facet` function _removeFacet(address _facet) private { DiamondStorage storage ds = getDiamondStorage(); // Get index of `DiamondStorage.facets` of the facet and last element of array uint256 facetPosition = ds.facetToSelectors[_facet].facetPosition; uint256 lastFacetPosition = ds.facets.length - 1; // If the facet is not at the end of the array then move the last element to the facet position if (facetPosition != lastFacetPosition) { address lastFacet = ds.facets[lastFacetPosition]; ds.facets[facetPosition] = lastFacet; ds.facetToSelectors[lastFacet].facetPosition = facetPosition.toUint16(); } // Remove last element from the facets array ds.facets.pop(); } /// @dev Delegates call to the initialization address with provided calldata /// @dev Used as a final step of diamond cut to execute the logic of the initialization for changed facets function _initializeDiamondCut(address _init, bytes memory _calldata) private { if (_init == address(0)) { require(_calldata.length == 0, "H"); // Non-empty calldata for zero address } else { // Do not check whether `_init` is a contract since later we check that it returns data. (bool success, bytes memory data) = _init.delegatecall(_calldata); require(success, "I"); // delegatecall failed // Check that called contract returns magic value to make sure that contract logic // supposed to be used as diamond cut initializer. require(data.length == 32, "lp"); require(abi.decode(data, (bytes32)) == DIAMOND_INIT_SUCCESS_RETURN_VALUE, "lp1"); } } }
File 3 of 4: ExecutorFacet
pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./interfaces/IAllowList.sol"; /// @author Matter Labs abstract contract AllowListed { modifier senderCanCallFunction(IAllowList _allowList) { // Preventing the stack too deep error { require(_allowList.canCall(msg.sender, address(this), msg.sig), "nr"); } _; } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT interface IAllowList { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /// @notice Access mode of target contract is changed event UpdateAccessMode(address indexed target, AccessMode previousMode, AccessMode newMode); /// @notice Permission to call is changed event UpdateCallPermission(address indexed caller, address indexed target, bytes4 indexed functionSig, bool status); /// @notice Type of access to a specific contract includes three different modes /// @param Closed No one has access to the contract /// @param SpecialAccessOnly Any address with granted special access can interact with a contract (see `hasSpecialAccessToCall`) /// @param Public Everyone can interact with a contract enum AccessMode { Closed, SpecialAccessOnly, Public } /// @dev A struct that contains deposit limit data of a token /// @param depositLimitation Whether any deposit limitation is placed or not /// @param depositCap The maximum amount that can be deposited. struct Deposit { bool depositLimitation; uint256 depositCap; } /*////////////////////////////////////////////////////////////// GETTERS //////////////////////////////////////////////////////////////*/ function getAccessMode(address _target) external view returns (AccessMode); function hasSpecialAccessToCall( address _caller, address _target, bytes4 _functionSig ) external view returns (bool); function canCall( address _caller, address _target, bytes4 _functionSig ) external view returns (bool); function getTokenDepositLimitData(address _l1Token) external view returns (Deposit memory); /*////////////////////////////////////////////////////////////// ALLOW LIST LOGIC //////////////////////////////////////////////////////////////*/ function setBatchAccessMode(address[] calldata _targets, AccessMode[] calldata _accessMode) external; function setAccessMode(address _target, AccessMode _accessMode) external; function setBatchPermissionToCall( address[] calldata _callers, address[] calldata _targets, bytes4[] calldata _functionSigs, bool[] calldata _enables ) external; function setPermissionToCall( address _caller, address _target, bytes4 _functionSig, bool _enable ) external; /*////////////////////////////////////////////////////////////// DEPOSIT LIMIT LOGIC //////////////////////////////////////////////////////////////*/ function setDepositLimit( address _l1Token, bool _depositLimitation, uint256 _depositCap ) external; } pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT /// @dev The address of the L2 deployer system contract. address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(0x8006); /// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed bytecode. /// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address. /// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer system contract /// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the `diamond-initializers` contracts. address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); /// @dev The address of the special smart contract that can send arbitrary length message as an L2 log address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(0x8008); /// @dev The formal address of the initial program of the system: the bootloader address constant L2_BOOTLOADER_ADDRESS = address(0x8001); /// @dev The address of the eth token system contract address constant L2_ETH_TOKEN_SYSTEM_CONTRACT_ADDR = address(0x800a); /// @dev The address of the known code storage system contract address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(0x8004); /// @dev The address of the context system contract address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(0x800b); pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT /** * @author Matter Labs * @notice Helper library for working with L2 contracts on L1. */ library L2ContractHelper { /// @dev The prefix used to create CREATE2 addresses. bytes32 constant CREATE2_PREFIX = keccak256("zksyncCreate2"); /// @notice Validate the bytecode format and calculate its hash. /// @param _bytecode The bytecode to hash. /// @return hashedBytecode The 32-byte hash of the bytecode. /// Note: The function reverts the execution if the bytecode has non expected format: /// - Bytecode bytes length is not a multiple of 32 /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words) /// - Bytecode words length is not odd function hashL2Bytecode(bytes memory _bytecode) internal pure returns (bytes32 hashedBytecode) { // Note that the length of the bytecode must be provided in 32-byte words. require(_bytecode.length % 32 == 0, "bl"); uint256 bytecodeLenInWords = _bytecode.length / 32; require(bytecodeLenInWords < 2**16, "pp"); // bytecode length must be less than 2^16 words require(bytecodeLenInWords % 2 == 1, "pr"); // bytecode length in words must be odd hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // Setting the version of the hash hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248))); // Setting the length hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224); } /// @notice Validates the format of the given bytecode hash. /// @dev Due to the specification of the L2 bytecode hash, not every 32 bytes could be a legit bytecode hash. /// @dev The function reverts on invalid bytecode hash formam. /// @param _bytecodeHash The hash of the bytecode to validate. function validateBytecodeHash(bytes32 _bytecodeHash) internal pure { uint8 version = uint8(_bytecodeHash[0]); require(version == 1 && _bytecodeHash[1] == bytes1(0), "zf"); // Incorrectly formatted bytecodeHash require(_bytecodeLen(_bytecodeHash) % 2 == 1, "uy"); // Code length in words must be odd } /// @notice Returns the length of the bytecode associated with the given hash. /// @param _bytecodeHash The hash of the bytecode. /// @return codeLengthInWords The length of the bytecode in words. function _bytecodeLen(bytes32 _bytecodeHash) private pure returns (uint256 codeLengthInWords) { codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3])); } /// @notice Computes the create2 address for a Layer 2 contract. /// @param _sender The address of the sender. /// @param _salt The salt value to use in the create2 address computation. /// @param _bytecodeHash The contract bytecode hash. /// @param _constructorInputHash The hash of the constructor input data. /// @return The create2 address of the contract. /// NOTE: L2 create2 derivation is different from L1 derivation! function computeCreate2Address( address _sender, bytes32 _salt, bytes32 _bytecodeHash, bytes32 _constructorInputHash ) internal pure returns (address) { bytes32 senderBytes = bytes32(uint256(uint160(_sender))); bytes32 data = keccak256( bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash) ); return address(uint160(uint256(data))); } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT library UncheckedMath { function uncheckedInc(uint256 _number) internal pure returns (uint256) { unchecked { return _number + 1; } } function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) { unchecked { return _lhs + _rhs; } } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT /** * @author Matter Labs * @dev The library provides a set of functions that help read data from an "abi.encodePacked" byte array. * @dev Each of the functions accepts the `bytes memory` and the offset where data should be read and returns a value of a certain type. * * @dev WARNING! * 1) Functions don't check the length of the bytes array, so it can go out of bounds. * The user of the library must check for bytes length before using any functions from the library! * * 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html. * Using data in inline assembly can lead to unexpected behavior! */ library UnsafeBytes { function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) { assembly { offset := add(_start, 4) result := mload(add(_bytes, offset)) } } function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) { assembly { offset := add(_start, 20) result := mload(add(_bytes, offset)) } } function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) { assembly { offset := add(_start, 32) result := mload(add(_bytes, offset)) } } function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) { assembly { offset := add(_start, 32) result := mload(add(_bytes, offset)) } } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. * * _Since v2.5.0:_ this module is now much more gas efficient, given net gas * metering changes introduced in the Istanbul hardfork. */ abstract contract ReentrancyGuard { /// @dev Address of lock flag variable. /// @dev Flag is placed at random memory location to not interfere with Storage contract. uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; // keccak256("ReentrancyGuard") - 1; // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/566a774222707e424896c0c390a84dc3c13bdcb2/contracts/security/ReentrancyGuard.sol // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; modifier reentrancyGuardInitializer() { _initializeReentrancyGuard(); _; } function _initializeReentrancyGuard() private { uint256 lockSlotOldValue; // Storing an initial non-zero value makes deployment a bit more // expensive but in exchange every call to nonReentrant // will be cheaper. assembly { lockSlotOldValue := sload(LOCK_FLAG_ADDRESS) sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED) } // Check that storage slot for reentrancy guard is empty to rule out possibility of slot conflict require(lockSlotOldValue == 0, "1B"); } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { uint256 _status; assembly { _status := sload(LOCK_FLAG_ADDRESS) } // On the first call to nonReentrant, _notEntered will be true require(_status == _NOT_ENTERED, "r1"); // Any calls to nonReentrant after this point will fail assembly { sstore(LOCK_FLAG_ADDRESS, _ENTERED) } _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) assembly { sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED) } } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT /// @dev `keccak256("")` bytes32 constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; /// @dev Bytes in raw L2 log /// @dev Equal to the bytes size of the tuple - (uint8 ShardId, bool isService, uint16 txNumberInBlock, address sender, bytes32 key, bytes32 value) uint256 constant L2_TO_L1_LOG_SERIALIZE_SIZE = 88; /// @dev The maximum length of the bytes array with L2 -> L1 logs uint256 constant MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES = 4 + L2_TO_L1_LOG_SERIALIZE_SIZE * 512; /// @dev L2 -> L1 logs Merkle tree height uint256 constant L2_TO_L1_LOG_MERKLE_TREE_HEIGHT = 9; /// @dev The value of default leaf hash for L2 -> L1 logs Merkle tree /// @dev An incomplete fixed-size tree is filled with this value to be a full binary tree /// @dev Actually equal to the `keccak256(new bytes(L2_TO_L1_LOG_SERIALIZE_SIZE))` bytes32 constant L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH = 0x72abee45b59e344af8a6e520241c4744aff26ed411f4c4b00f8af09adada43ba; /// @dev Number of bytes in a one initial storage change /// @dev Equal to the bytes size of the tuple - (bytes32 key, bytes32 value) uint256 constant INITIAL_STORAGE_CHANGE_SERIALIZE_SIZE = 64; /// @dev The maximum length of the bytes array with initial storage changes uint256 constant MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + INITIAL_STORAGE_CHANGE_SERIALIZE_SIZE * 4765; /// @dev Number of bytes in a one repeated storage change /// @dev Equal to the bytes size of the tuple - (bytes8 key, bytes32 value) uint256 constant REPEATED_STORAGE_CHANGE_SERIALIZE_SIZE = 40; /// @dev The maximum length of the bytes array with repeated storage changes uint256 constant MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES = 4 + REPEATED_STORAGE_CHANGE_SERIALIZE_SIZE * 7564; // TODO: change constant to the real root hash of empty Merkle tree (SMA-184) bytes32 constant DEFAULT_L2_LOGS_TREE_ROOT_HASH = bytes32(0); /// @dev Denotes the type of the zkSync transaction that came from L1. uint256 constant PRIORITY_OPERATION_L2_TX_TYPE = 255; /// @dev Denotes the type of the zkSync transaction that is used for system upgrades. uint256 constant SYSTEM_UPGRADE_L2_TX_TYPE = 254; /// @dev The amount of time in seconds the validator has to process the priority transaction /// NOTE: The constant is set to zero for the Alpha release period uint256 constant PRIORITY_EXPIRATION = 0 days; /// @dev Notice period before activation preparation status of upgrade mode (in seconds) /// @dev NOTE: we must reserve for users enough time to send full exit operation, wait maximum time for processing this operation and withdraw funds from it. uint256 constant UPGRADE_NOTICE_PERIOD = 0; /// @dev Timestamp - seconds since unix epoch uint256 constant COMMIT_TIMESTAMP_NOT_OLDER = 365 days; /// @dev Maximum available error between real commit block timestamp and analog used in the verifier (in seconds) /// @dev Must be used cause miner's `block.timestamp` value can differ on some small value (as we know - 15 seconds) uint256 constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 365 days; /// @dev Bit mask to apply for verifier public input before verifying. uint256 constant INPUT_MASK = 452312848583266388373324160190187140051835877600158453279131187530910662655; /// @dev The maximum number of L2 gas that a user can request for an L2 transaction uint256 constant L2_TX_MAX_GAS_LIMIT = 80000000; /// @dev The maximum number of the pubdata an L2 operation should be allowed to use. uint256 constant MAX_PUBDATA_PER_BLOCK = 110000; /// @dev The maximum number of the pubdata an priority operation should be allowed to use. /// For now, it is somewhat lower than the maximum number of pubdata allowed for an L2 transaction, /// to ensure that the transaction is definitely processable on L2 despite any potential overhead. uint256 constant PRIORITY_TX_MAX_PUBDATA = 99000; /// @dev The default price per L2 gas to be used for L1->L2 transactions uint256 constant FAIR_L2_GAS_PRICE = 500000000; /// @dev Even though the price for 1 byte of pubdata is 16 L1 gas, we have a slightly increased /// value. uint256 constant L1_GAS_PER_PUBDATA_BYTE = 17; /// @dev The computational overhead of processing an L2 block. uint256 constant BLOCK_OVERHEAD_L2_GAS = 1200000; /// @dev The overhead in L1 gas of interacting with the L1 uint256 constant BLOCK_OVERHEAD_L1_GAS = 1000000; /// @dev The equivalent in L1 pubdata of L1 gas used for working with L1 uint256 constant BLOCK_OVERHEAD_PUBDATA = BLOCK_OVERHEAD_L1_GAS / L1_GAS_PER_PUBDATA_BYTE; /// @dev The maximum number of transactions in L2 block: uint256 constant MAX_TRANSACTIONS_IN_BLOCK = 1024; /// @dev The size of the bootloader memory dedicated to the encodings of transactions uint256 constant BOOTLOADER_TX_ENCODING_SPACE = 485225; /// @dev The intrinsic cost of the L1->l2 transaction in computational L2 gas uint256 constant L1_TX_INTRINSIC_L2_GAS = 167157; /// @dev The intrinsic cost of the L1->l2 transaction in pubdata uint256 constant L1_TX_INTRINSIC_PUBDATA = 88; /// @dev The minimal base price for L1 transaction uint256 constant L1_TX_MIN_L2_GAS_BASE = 173484; /// @dev The number of L2 gas the transaction starts costing more with each 544 bytes of encoding uint256 constant L1_TX_DELTA_544_ENCODING_BYTES = 1656; /// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency uint256 constant L1_TX_DELTA_FACTORY_DEPS_L2_GAS = 2473; /// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency uint256 constant L1_TX_DELTA_FACTORY_DEPS_PUBDATA = 64; /// @dev The number of pubdata an L1->L2 transaction requires with each new factory dependency uint256 constant MAX_NEW_FACTORY_DEPS = 32; /// @dev The L2 gasPricePerPubdata required to be used in bridges. uint256 constant REQUIRED_L2_GAS_PRICE_PER_PUBDATA = 800; /// @dev The mask which should be applied to the packed batch and L2 block timestamp in order /// to obtain the L2 block timestamp. Applying this mask is equivalent to calculating modulo 2**128 uint256 constant PACKED_L2_BLOCK_TIMESTAMP_MASK = 0xffffffffffffffffffffffffffffffff; pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "../Storage.sol"; import "../../common/ReentrancyGuard.sol"; import "../../common/AllowListed.sol"; /// @title Base contract containing functions accessible to the other facets. /// @author Matter Labs contract Base is ReentrancyGuard, AllowListed { AppStorage internal s; /// @notice Checks that the message sender is an active governor modifier onlyGovernor() { require(msg.sender == s.governor, "1g"); // only by governor _; } /// @notice Checks if validator is active modifier onlyValidator() { require(s.validators[msg.sender], "1h"); // validator is not active _; } /// @notice Checks if `msg.sender` is the security council modifier onlySecurityCouncil() { require(msg.sender == s.upgrades.securityCouncil, "a9"); // not a security council _; } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import {Base} from "./Base.sol"; import {COMMIT_TIMESTAMP_NOT_OLDER, COMMIT_TIMESTAMP_APPROXIMATION_DELTA, EMPTY_STRING_KECCAK, L2_TO_L1_LOG_SERIALIZE_SIZE, INPUT_MASK, MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES, MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES, MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, PACKED_L2_BLOCK_TIMESTAMP_MASK} from "../Config.sol"; import {IExecutor} from "../interfaces/IExecutor.sol"; import {PairingsBn254} from "../libraries/PairingsBn254.sol"; import {PriorityQueue, PriorityOperation} from "../libraries/PriorityQueue.sol"; import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol"; import {UnsafeBytes} from "../../common/libraries/UnsafeBytes.sol"; import {L2ContractHelper} from "../../common/libraries/L2ContractHelper.sol"; import {VerifierParams} from "../Storage.sol"; import {L2_BOOTLOADER_ADDRESS, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR, L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR} from "../../common/L2ContractAddresses.sol"; /// @title zkSync Executor contract capable of processing events emitted in the zkSync protocol. /// @author Matter Labs contract ExecutorFacet is Base, IExecutor { using UncheckedMath for uint256; using PriorityQueue for PriorityQueue.Queue; string public constant override getName = "ExecutorFacet"; /// @dev Process one block commit using the previous block StoredBlockInfo /// @dev returns new block StoredBlockInfo /// @notice Does not change storage function _commitOneBlock( StoredBlockInfo memory _previousBlock, CommitBlockInfo calldata _newBlock, bytes32 _expectedSystemContractUpgradeTxHash ) internal view returns (StoredBlockInfo memory) { require(_newBlock.blockNumber == _previousBlock.blockNumber + 1, "f"); // only commit next block // Check that block contain all meta information for L2 logs. // Get the chained hash of priority transaction hashes. ( uint256 expectedNumberOfLayer1Txs, bytes32 expectedPriorityOperationsHash, bytes32 previousBlockHash, uint256 packedBatchAndL2BlockTimestamp ) = _processL2Logs(_newBlock, _expectedSystemContractUpgradeTxHash); require(_previousBlock.blockHash == previousBlockHash, "l"); // Check that the priority operation hash in the L2 logs is as expected require(expectedPriorityOperationsHash == _newBlock.priorityOperationsHash, "t"); // Check that the number of processed priority operations is as expected require(expectedNumberOfLayer1Txs == _newBlock.numberOfLayer1Txs, "ta"); // Check the timestamp of the new block _verifyBlockTimestamp(packedBatchAndL2BlockTimestamp, _newBlock.timestamp, _previousBlock.timestamp); // Preventing "stack too deep error" { // Check the index of repeated storage writes uint256 newStorageChangesIndexes = uint256(uint32(bytes4(_newBlock.initialStorageChanges[:4]))); require( _previousBlock.indexRepeatedStorageChanges + newStorageChangesIndexes == _newBlock.indexRepeatedStorageChanges, "yq" ); } // Create block commitment for the proof verification bytes32 commitment = _createBlockCommitment(_newBlock); return StoredBlockInfo( _newBlock.blockNumber, _newBlock.newStateRoot, _newBlock.indexRepeatedStorageChanges, _newBlock.numberOfLayer1Txs, _newBlock.priorityOperationsHash, _newBlock.l2LogsTreeRoot, _newBlock.timestamp, commitment ); } /// @notice checks that the timestamps of both the new batch and the new L2 block are correct. /// @param _packedBatchAndL2BlockTimestamp - packed batch and L2 block timestamp in a foramt of batchTimestamp * 2**128 + l2BlockTimestamp /// @param _expectedBatchTimestamp - expected batch timestamp /// @param _previousBatchTimestamp - the timestamp of the previous batch function _verifyBlockTimestamp( uint256 _packedBatchAndL2BlockTimestamp, uint256 _expectedBatchTimestamp, uint256 _previousBatchTimestamp ) internal view { // Check that the timestamp that came from the system context is expected uint256 batchTimestamp = _packedBatchAndL2BlockTimestamp >> 128; require(batchTimestamp == _expectedBatchTimestamp, "tb"); // While the fact that _previousBatchTimestamp < batchTimestamp is already checked on L2, // we double check it here for clarity require(_previousBatchTimestamp < batchTimestamp, "h"); uint256 lastL2BlockTimestamp = _packedBatchAndL2BlockTimestamp & PACKED_L2_BLOCK_TIMESTAMP_MASK; // On L2, all blocks have timestamps within the range of [batchTimestamp, lastL2BlockTimestamp]. // So here we need to only double check that: // - The timestamp of the batch is not too small. // - The timestamp of the last L2 block is not too big. require(block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER <= batchTimestamp, "h1"); // New batch timestamp is too small require(lastL2BlockTimestamp <= block.timestamp + COMMIT_TIMESTAMP_APPROXIMATION_DELTA, "h2"); // The last L2 block timestamp is too big } /// @dev Check that L2 logs are proper and block contain all meta information for them function _processL2Logs(CommitBlockInfo calldata _newBlock, bytes32 _expectedSystemContractUpgradeTxHash) internal pure returns ( uint256 numberOfLayer1Txs, bytes32 chainedPriorityTxsHash, bytes32 previousBlockHash, uint256 packedBatchAndL2BlockTimestamp ) { // Copy L2 to L1 logs into memory. bytes memory emittedL2Logs = _newBlock.l2Logs[4:]; uint256 currentMessage; // Auxiliary variable that is needed to enforce that `previousBlockHash` and `blockTimestamp` was read exactly one time bool isSystemContextLogProcessed; bytes[] calldata factoryDeps = _newBlock.factoryDeps; uint256 currentBytecode; chainedPriorityTxsHash = EMPTY_STRING_KECCAK; // linear traversal of the logs for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) { (address logSender, ) = UnsafeBytes.readAddress(emittedL2Logs, i + 4); // show preimage for hashed message stored in log if (logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) { (bytes32 hashedMessage, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 56); require(keccak256(_newBlock.l2ArbitraryLengthMessages[currentMessage]) == hashedMessage, "k2"); currentMessage = currentMessage.uncheckedInc(); } else if (logSender == L2_BOOTLOADER_ADDRESS) { (bytes32 canonicalTxHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 24); if (_expectedSystemContractUpgradeTxHash != bytes32(0)) { require(_expectedSystemContractUpgradeTxHash == canonicalTxHash, "bz"); _expectedSystemContractUpgradeTxHash = bytes32(0); } else { chainedPriorityTxsHash = keccak256(abi.encode(chainedPriorityTxsHash, canonicalTxHash)); // Overflow is not realistic numberOfLayer1Txs = numberOfLayer1Txs.uncheckedInc(); } } else if (logSender == L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR) { // Make sure that the system context log wasn't processed yet, to // avoid accident double reading `blockTimestamp` and `previousBlockHash` require(!isSystemContextLogProcessed, "fx"); (packedBatchAndL2BlockTimestamp, ) = UnsafeBytes.readUint256(emittedL2Logs, i + 24); (previousBlockHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 56); // Mark system context log as processed isSystemContextLogProcessed = true; } else if (logSender == L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR) { (bytes32 bytecodeHash, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + 24); require(bytecodeHash == L2ContractHelper.hashL2Bytecode(factoryDeps[currentBytecode]), "k3"); currentBytecode = currentBytecode.uncheckedInc(); } else { // Only some system contracts could send raw logs from L2 to L1, double check that invariant holds here. revert("ne"); } } // To check that only relevant preimages have been included in the calldata require(currentBytecode == factoryDeps.length, "ym"); require(currentMessage == _newBlock.l2ArbitraryLengthMessages.length, "pl"); // `blockTimestamp` and `previousBlockHash` wasn't read from L2 logs require(isSystemContextLogProcessed, "by"); // Making sure that the system contract upgrade was included if needed require(_expectedSystemContractUpgradeTxHash == bytes32(0), "bw"); } /// @notice Commit block /// @notice 1. Checks timestamp. /// @notice 2. Process L2 logs. /// @notice 3. Store block commitments. function commitBlocks(StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData) external override nonReentrant onlyValidator { // Check that we commit blocks after last committed block require(s.storedBlockHashes[s.totalBlocksCommitted] == _hashStoredBlockInfo(_lastCommittedBlockData), "i"); // incorrect previous block data require(_newBlocksData.length > 0, "No blocks to commit"); bytes32 systemContractsUpgradeTxHash = s.l2SystemContractsUpgradeTxHash; // Upgrades are rarely done so we optimize a case with no active system contracts upgrade. if (systemContractsUpgradeTxHash == bytes32(0) || s.l2SystemContractsUpgradeBlockNumber != 0) { _commitBlocksWithoutSystemContractsUpgrade(_lastCommittedBlockData, _newBlocksData); } else { _commitBlocksWithSystemContractsUpgrade( _lastCommittedBlockData, _newBlocksData, systemContractsUpgradeTxHash ); } s.totalBlocksCommitted = s.totalBlocksCommitted + _newBlocksData.length; } /// @dev Commits new blocks without any system contracts upgrade. /// @param _lastCommittedBlockData The data of the last committed block. /// @param _newBlocksData An array of block data that needs to be committed. function _commitBlocksWithoutSystemContractsUpgrade( StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData ) internal { for (uint256 i = 0; i < _newBlocksData.length; i = i.uncheckedInc()) { _lastCommittedBlockData = _commitOneBlock(_lastCommittedBlockData, _newBlocksData[i], bytes32(0)); s.storedBlockHashes[_lastCommittedBlockData.blockNumber] = _hashStoredBlockInfo(_lastCommittedBlockData); emit BlockCommit( _lastCommittedBlockData.blockNumber, _lastCommittedBlockData.blockHash, _lastCommittedBlockData.commitment ); } } /// @dev Commits new blocks with a system contracts upgrade transaction. /// @param _lastCommittedBlockData The data of the last committed block. /// @param _newBlocksData An array of block data that needs to be committed. /// @param _systemContractUpgradeTxHash The transaction hash of the system contract upgrade. function _commitBlocksWithSystemContractsUpgrade( StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData, bytes32 _systemContractUpgradeTxHash ) internal { // The system contract upgrade is designed to be executed atomically with the new bootloader, a default account, // ZKP verifier, and other system parameters. Hence, we ensure that the upgrade transaction is // carried out within the first block committed after the upgrade. // While the logic of the contract ensures that the s.l2SystemContractsUpgradeBlockNumber is 0 when this function is called, // this check is added just in case. Since it is a hot read, it does not encure noticable gas cost. require(s.l2SystemContractsUpgradeBlockNumber == 0, "ik"); // Save the block number where the upgrade transaction was executed. s.l2SystemContractsUpgradeBlockNumber = _newBlocksData[0].blockNumber; for (uint256 i = 0; i < _newBlocksData.length; i = i.uncheckedInc()) { // The upgrade transaction must only be included in the first block. bytes32 expectedUpgradeTxHash = i == 0 ? _systemContractUpgradeTxHash : bytes32(0); _lastCommittedBlockData = _commitOneBlock( _lastCommittedBlockData, _newBlocksData[i], expectedUpgradeTxHash ); s.storedBlockHashes[_lastCommittedBlockData.blockNumber] = _hashStoredBlockInfo(_lastCommittedBlockData); emit BlockCommit( _lastCommittedBlockData.blockNumber, _lastCommittedBlockData.blockHash, _lastCommittedBlockData.commitment ); } } /// @dev Pops the priority operations from the priority queue and returns a rolling hash of operations function _collectOperationsFromPriorityQueue(uint256 _nPriorityOps) internal returns (bytes32 concatHash) { concatHash = EMPTY_STRING_KECCAK; for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) { PriorityOperation memory priorityOp = s.priorityQueue.popFront(); concatHash = keccak256(abi.encode(concatHash, priorityOp.canonicalTxHash)); } } /// @dev Executes one block /// @dev 1. Processes all pending operations (Complete priority requests) /// @dev 2. Finalizes block on Ethereum /// @dev _executedBlockIdx is an index in the array of the blocks that we want to execute together function _executeOneBlock(StoredBlockInfo memory _storedBlock, uint256 _executedBlockIdx) internal { uint256 currentBlockNumber = _storedBlock.blockNumber; require(currentBlockNumber == s.totalBlocksExecuted + _executedBlockIdx + 1, "k"); // Execute blocks in order require( _hashStoredBlockInfo(_storedBlock) == s.storedBlockHashes[currentBlockNumber], "exe10" // executing block should be committed ); bytes32 priorityOperationsHash = _collectOperationsFromPriorityQueue(_storedBlock.numberOfLayer1Txs); require(priorityOperationsHash == _storedBlock.priorityOperationsHash, "x"); // priority operations hash does not match to expected // Save root hash of L2 -> L1 logs tree s.l2LogsRootHashes[currentBlockNumber] = _storedBlock.l2LogsTreeRoot; } /// @notice Execute blocks, complete priority operations and process withdrawals. /// @notice 1. Processes all pending operations (Complete priority requests) /// @notice 2. Finalizes block on Ethereum function executeBlocks(StoredBlockInfo[] calldata _blocksData) external nonReentrant onlyValidator { uint256 nBlocks = _blocksData.length; for (uint256 i = 0; i < nBlocks; i = i.uncheckedInc()) { _executeOneBlock(_blocksData[i], i); emit BlockExecution(_blocksData[i].blockNumber, _blocksData[i].blockHash, _blocksData[i].commitment); } uint256 newTotalBlocksExecuted = s.totalBlocksExecuted + nBlocks; s.totalBlocksExecuted = newTotalBlocksExecuted; require(newTotalBlocksExecuted <= s.totalBlocksVerified, "n"); // Can't execute blocks more than committed and proven currently. uint256 blockWhenUpgradeHappened = s.l2SystemContractsUpgradeBlockNumber; if (blockWhenUpgradeHappened != 0 && blockWhenUpgradeHappened <= newTotalBlocksExecuted) { delete s.l2SystemContractsUpgradeTxHash; delete s.l2SystemContractsUpgradeBlockNumber; } } /// @notice Blocks commitment verification. /// @notice Only verifies block commitments without any other processing function proveBlocks( StoredBlockInfo calldata _prevBlock, StoredBlockInfo[] calldata _committedBlocks, ProofInput calldata _proof ) external nonReentrant onlyValidator { // Save the variables into the stack to save gas on reading them later uint256 currentTotalBlocksVerified = s.totalBlocksVerified; uint256 committedBlocksLength = _committedBlocks.length; // Save the variable from the storage to memory to save gas VerifierParams memory verifierParams = s.verifierParams; // Initialize the array, that will be used as public input to the ZKP uint256[] memory proofPublicInput = new uint256[](committedBlocksLength); // Check that the block passed by the validator is indeed the first unverified block require(_hashStoredBlockInfo(_prevBlock) == s.storedBlockHashes[currentTotalBlocksVerified], "t1"); bytes32 prevBlockCommitment = _prevBlock.commitment; for (uint256 i = 0; i < committedBlocksLength; i = i.uncheckedInc()) { currentTotalBlocksVerified = currentTotalBlocksVerified.uncheckedInc(); require(_hashStoredBlockInfo(_committedBlocks[i]) == s.storedBlockHashes[currentTotalBlocksVerified], "o1"); bytes32 currentBlockCommitment = _committedBlocks[i].commitment; proofPublicInput[i] = _getBlockProofPublicInput( prevBlockCommitment, currentBlockCommitment, _proof, verifierParams ); prevBlockCommitment = currentBlockCommitment; } require(currentTotalBlocksVerified <= s.totalBlocksCommitted, "q"); bool successVerifyProof = s.verifier.verify_serialized_proof(proofPublicInput, _proof.serializedProof); require(successVerifyProof, "p"); // Proof verification fail // Verify the recursive part that was given to us through the public input bool successProofAggregation = _verifyRecursivePartOfProof(_proof.recursiveAggregationInput); require(successProofAggregation, "hh"); // Proof aggregation must be valid emit BlocksVerification(s.totalBlocksVerified, currentTotalBlocksVerified); s.totalBlocksVerified = currentTotalBlocksVerified; } /// @dev Gets zk proof public input function _getBlockProofPublicInput( bytes32 _prevBlockCommitment, bytes32 _currentBlockCommitment, ProofInput calldata _proof, VerifierParams memory _verifierParams ) internal pure returns (uint256) { return uint256( keccak256( abi.encodePacked( _prevBlockCommitment, _currentBlockCommitment, _verifierParams.recursionNodeLevelVkHash, _verifierParams.recursionLeafLevelVkHash, _verifierParams.recursionCircuitsSetVksHash, _proof.recursiveAggregationInput ) ) ) & INPUT_MASK; } /// @dev Verify a part of the zkp, that is responsible for the aggregation function _verifyRecursivePartOfProof(uint256[] calldata _recursiveAggregationInput) internal view returns (bool) { require(_recursiveAggregationInput.length == 4, "vr"); PairingsBn254.G1Point memory pairWithGen = PairingsBn254.new_g1_checked( _recursiveAggregationInput[0], _recursiveAggregationInput[1] ); PairingsBn254.G1Point memory pairWithX = PairingsBn254.new_g1_checked( _recursiveAggregationInput[2], _recursiveAggregationInput[3] ); PairingsBn254.G2Point memory g2Gen = PairingsBn254.new_g2( [ 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed ], [ 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa ] ); PairingsBn254.G2Point memory g2X = PairingsBn254.new_g2( [ 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1, 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 ], [ 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4, 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 ] ); return PairingsBn254.pairingProd2(pairWithGen, g2Gen, pairWithX, g2X); } /// @notice Reverts unexecuted blocks /// @param _newLastBlock block number after which blocks should be reverted /// NOTE: Doesn't delete the stored data about blocks, but only decreases /// counters that are responsible for the number of blocks function revertBlocks(uint256 _newLastBlock) external nonReentrant onlyValidator { require(s.totalBlocksCommitted > _newLastBlock, "v1"); // The last committed block is less than new last block uint256 newTotalBlocksCommitted = _maxU256(_newLastBlock, s.totalBlocksExecuted); if (newTotalBlocksCommitted < s.totalBlocksVerified) { s.totalBlocksVerified = newTotalBlocksCommitted; } s.totalBlocksCommitted = newTotalBlocksCommitted; // Reset the block number of the executed system contracts upgrade transaction if the block // where the system contracts upgrade was committed is among the reverted blocks. if (s.l2SystemContractsUpgradeBlockNumber > newTotalBlocksCommitted) { delete s.l2SystemContractsUpgradeBlockNumber; } emit BlocksRevert(s.totalBlocksCommitted, s.totalBlocksVerified, s.totalBlocksExecuted); } /// @notice Returns larger of two values function _maxU256(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? b : a; } /// @dev Creates block commitment from its data function _createBlockCommitment(CommitBlockInfo calldata _newBlockData) internal view returns (bytes32) { bytes32 passThroughDataHash = keccak256(_blockPassThroughData(_newBlockData)); bytes32 metadataHash = keccak256(_blockMetaParameters()); bytes32 auxiliaryOutputHash = keccak256(_blockAuxiliaryOutput(_newBlockData)); return keccak256(abi.encode(passThroughDataHash, metadataHash, auxiliaryOutputHash)); } function _blockPassThroughData(CommitBlockInfo calldata _block) internal pure returns (bytes memory) { return abi.encodePacked( _block.indexRepeatedStorageChanges, _block.newStateRoot, uint64(0), // index repeated storage changes in zkPorter bytes32(0) // zkPorter block hash ); } function _blockMetaParameters() internal view returns (bytes memory) { return abi.encodePacked(s.zkPorterIsAvailable, s.l2BootloaderBytecodeHash, s.l2DefaultAccountBytecodeHash); } function _blockAuxiliaryOutput(CommitBlockInfo calldata _block) internal pure returns (bytes memory) { require(_block.initialStorageChanges.length <= MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES, "pf"); require(_block.repeatedStorageChanges.length <= MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES, "py"); require(_block.l2Logs.length <= MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, "pu"); bytes32 initialStorageChangesHash = keccak256(_block.initialStorageChanges); bytes32 repeatedStorageChangesHash = keccak256(_block.repeatedStorageChanges); bytes32 l2ToL1LogsHash = keccak256(_block.l2Logs); return abi.encode(_block.l2LogsTreeRoot, l2ToL1LogsHash, initialStorageChangesHash, repeatedStorageChangesHash); } /// @notice Returns the keccak hash of the ABI-encoded StoredBlockInfo function _hashStoredBlockInfo(StoredBlockInfo memory _storedBlockInfo) internal pure returns (bytes32) { return keccak256(abi.encode(_storedBlockInfo)); } } pragma solidity ^0.8.0; // SPDX-License-Identifier: UNLICENSED interface IBase { function getName() external view returns (string memory); } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./IBase.sol"; interface IExecutor is IBase { /// @notice Rollup block stored data /// @param blockNumber Rollup block number /// @param blockHash Hash of L2 block /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more /// @param numberOfLayer1Txs Number of priority operations to be processed /// @param priorityOperationsHash Hash of all priority operations from this block /// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this block /// @param timestamp Rollup block timestamp, have the same format as Ethereum block constant /// @param commitment Verified input for the zkSync circuit struct StoredBlockInfo { uint64 blockNumber; bytes32 blockHash; uint64 indexRepeatedStorageChanges; uint256 numberOfLayer1Txs; bytes32 priorityOperationsHash; bytes32 l2LogsTreeRoot; uint256 timestamp; bytes32 commitment; } /// @notice Data needed to commit new block /// @param blockNumber Number of the committed block /// @param timestamp Unix timestamp denoting the start of the block execution /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more /// @param newStateRoot The state root of the full state tree /// @param numberOfLayer1Txs Number of priority operations to be processed /// @param l2LogsTreeRoot The root hash of the tree that contains all L2 -> L1 logs in the block /// @param priorityOperationsHash Hash of all priority operations from this block /// @param initialStorageChanges Storage write access as a concatenation key-value /// @param repeatedStorageChanges Storage write access as a concatenation index-value /// @param l2Logs concatenation of all L2 -> L1 logs in the block /// @param l2ArbitraryLengthMessages array of hash preimages that were sent as value of L2 logs by special system L2 contract /// @param factoryDeps array of l2 bytecodes that were marked as known on L2 struct CommitBlockInfo { uint64 blockNumber; uint64 timestamp; uint64 indexRepeatedStorageChanges; bytes32 newStateRoot; uint256 numberOfLayer1Txs; bytes32 l2LogsTreeRoot; bytes32 priorityOperationsHash; bytes initialStorageChanges; bytes repeatedStorageChanges; bytes l2Logs; bytes[] l2ArbitraryLengthMessages; bytes[] factoryDeps; } /// @notice Recursive proof input data (individual commitments are constructed onchain) struct ProofInput { uint256[] recursiveAggregationInput; uint256[] serializedProof; } function commitBlocks(StoredBlockInfo calldata _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData) external; function proveBlocks( StoredBlockInfo calldata _prevBlock, StoredBlockInfo[] calldata _committedBlocks, ProofInput calldata _proof ) external; function executeBlocks(StoredBlockInfo[] calldata _blocksData) external; function revertBlocks(uint256 _newLastBlock) external; /// @notice Event emitted when a block is committed event BlockCommit(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment); /// @notice Event emitted when blocks are verified event BlocksVerification(uint256 indexed previousLastVerifiedBlock, uint256 indexed currentLastVerifiedBlock); /// @notice Event emitted when a block is executed event BlockExecution(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed commitment); /// @notice Event emitted when blocks are reverted event BlocksRevert(uint256 totalBlocksCommitted, uint256 totalBlocksVerified, uint256 totalBlocksExecuted); } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT library PairingsBn254 { uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583; uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint256 constant bn254_b_coeff = 3; struct G1Point { uint256 X; uint256 Y; } struct Fr { uint256 value; } function new_fr(uint256 fr) internal pure returns (Fr memory) { require(fr < r_mod); return Fr({value: fr}); } function copy(Fr memory self) internal pure returns (Fr memory n) { n.value = self.value; } function assign(Fr memory self, Fr memory other) internal pure { self.value = other.value; } function inverse(Fr memory fr) internal view returns (Fr memory) { require(fr.value != 0); return pow(fr, r_mod - 2); } function add_assign(Fr memory self, Fr memory other) internal pure { self.value = addmod(self.value, other.value, r_mod); } function sub_assign(Fr memory self, Fr memory other) internal pure { self.value = addmod(self.value, r_mod - other.value, r_mod); } function mul_assign(Fr memory self, Fr memory other) internal pure { self.value = mulmod(self.value, other.value, r_mod); } function pow(Fr memory self, uint256 power) internal view returns (Fr memory) { uint256[6] memory input = [32, 32, 32, self.value, power, r_mod]; uint256[1] memory result; bool success; assembly { success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20) } require(success); return Fr({value: result[0]}); } // Encoding of field elements is: X[0] * z + X[1] struct G2Point { uint256[2] X; uint256[2] Y; } function P1() internal pure returns (G1Point memory) { return G1Point(1, 2); } function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) { return G1Point(x, y); } // function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) { function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) { if (x == 0 && y == 0) { // point of infinity is (0,0) return G1Point(x, y); } // check encoding require(x < q_mod, "x axis isn't valid"); require(y < q_mod, "y axis isn't valid"); // check on curve uint256 lhs = mulmod(y, y, q_mod); // y^2 uint256 rhs = mulmod(x, x, q_mod); // x^2 rhs = mulmod(rhs, x, q_mod); // x^3 rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b require(lhs == rhs, "is not on curve"); return G1Point(x, y); } function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) { return G2Point(x, y); } function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) { result.X = self.X; result.Y = self.Y; } function P2() internal pure returns (G2Point memory) { // for some reason ethereum expects to have c1*v + c0 form return G2Point( [ 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed ], [ 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa ] ); } function negate(G1Point memory self) internal pure { // The prime q in the base field F_q for G1 if (self.Y == 0) { require(self.X == 0); return; } self.Y = q_mod - self.Y; } function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { point_add_into_dest(p1, p2, r); return r; } function point_add_assign(G1Point memory p1, G1Point memory p2) internal view { point_add_into_dest(p1, p2, p1); } function point_add_into_dest( G1Point memory p1, G1Point memory p2, G1Point memory dest ) internal view { if (p2.X == 0 && p2.Y == 0) { // we add zero, nothing happens dest.X = p1.X; dest.Y = p1.Y; return; } else if (p1.X == 0 && p1.Y == 0) { // we add into zero, and we add non-zero point dest.X = p2.X; dest.Y = p2.Y; return; } else { uint256[4] memory input; input[0] = p1.X; input[1] = p1.Y; input[2] = p2.X; input[3] = p2.Y; bool success; assembly { success := staticcall(gas(), 6, input, 0x80, dest, 0x40) } require(success); } } function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view { point_sub_into_dest(p1, p2, p1); } function point_sub_into_dest( G1Point memory p1, G1Point memory p2, G1Point memory dest ) internal view { if (p2.X == 0 && p2.Y == 0) { // we subtracted zero, nothing happens dest.X = p1.X; dest.Y = p1.Y; return; } else if (p1.X == 0 && p1.Y == 0) { // we subtract from zero, and we subtract non-zero point dest.X = p2.X; dest.Y = q_mod - p2.Y; return; } else { uint256[4] memory input; input[0] = p1.X; input[1] = p1.Y; input[2] = p2.X; input[3] = q_mod - p2.Y; bool success = false; assembly { success := staticcall(gas(), 6, input, 0x80, dest, 0x40) } require(success); } } function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) { // https://eips.ethereum.org/EIPS/eip-197 // Elliptic curve points are encoded as a Jacobian pair (X, Y) where the point at infinity is encoded as (0, 0) if (p.X == 0 && p.Y == 1) { p.Y = 0; } point_mul_into_dest(p, s, r); return r; } function point_mul_assign(G1Point memory p, Fr memory s) internal view { point_mul_into_dest(p, s, p); } function point_mul_into_dest( G1Point memory p, Fr memory s, G1Point memory dest ) internal view { uint256[3] memory input; input[0] = p.X; input[1] = p.Y; input[2] = s.value; bool success; assembly { success := staticcall(gas(), 7, input, 0x60, dest, 0x40) } require(success); } function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) { require(p1.length == p2.length); uint256 elements = p1.length; uint256 inputSize = elements * 6; uint256[] memory input = new uint256[](inputSize); for (uint256 i = 0; i < elements; ) { input[i * 6 + 0] = p1[i].X; input[i * 6 + 1] = p1[i].Y; input[i * 6 + 2] = p2[i].X[0]; input[i * 6 + 3] = p2[i].X[1]; input[i * 6 + 4] = p2[i].Y[0]; input[i * 6 + 5] = p2[i].Y[1]; unchecked { ++i; } } uint256[1] memory out; bool success; assembly { success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) } require(success); return out[0] != 0; } /// Convenience method for a pairing check for two pairs. function pairingProd2( G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2 ) internal view returns (bool) { G1Point[] memory p1 = new G1Point[](2); G2Point[] memory p2 = new G2Point[](2); p1[0] = a1; p1[1] = b1; p2[0] = a2; p2[1] = b2; return pairing(p1, p2); } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT /// @notice The structure that contains meta information of the L2 transaction that was requested from L1 /// @dev The weird size of fields was selected specifically to minimize the structure storage size /// @param canonicalTxHash Hashed L2 transaction data that is needed to process it /// @param expirationTimestamp Expiration timestamp for this request (must be satisfied before) /// @param layer2Tip Additional payment to the validator as an incentive to perform the operation struct PriorityOperation { bytes32 canonicalTxHash; uint64 expirationTimestamp; uint192 layer2Tip; } /// @author Matter Labs /// @dev The library provides the API to interact with the priority queue container /// @dev Order of processing operations from queue - FIFO (Fist in - first out) library PriorityQueue { using PriorityQueue for Queue; /// @notice Container that stores priority operations /// @param data The inner mapping that saves priority operation by its index /// @param head The pointer to the first unprocessed priority operation, equal to the tail if the queue is empty /// @param tail The pointer to the free slot struct Queue { mapping(uint256 => PriorityOperation) data; uint256 tail; uint256 head; } /// @notice Returns zero if and only if no operations were processed from the queue /// @return Index of the oldest priority operation that wasn't processed yet function getFirstUnprocessedPriorityTx(Queue storage _queue) internal view returns (uint256) { return _queue.head; } /// @return The total number of priority operations that were added to the priority queue, including all processed ones function getTotalPriorityTxs(Queue storage _queue) internal view returns (uint256) { return _queue.tail; } /// @return The total number of unprocessed priority operations in a priority queue function getSize(Queue storage _queue) internal view returns (uint256) { return uint256(_queue.tail - _queue.head); } /// @return Whether the priority queue contains no operations function isEmpty(Queue storage _queue) internal view returns (bool) { return _queue.tail == _queue.head; } /// @notice Add the priority operation to the end of the priority queue function pushBack(Queue storage _queue, PriorityOperation memory _operation) internal { // Save value into the stack to avoid double reading from the storage uint256 tail = _queue.tail; _queue.data[tail] = _operation; _queue.tail = tail + 1; } /// @return The first unprocessed priority operation from the queue function front(Queue storage _queue) internal view returns (PriorityOperation memory) { require(!_queue.isEmpty(), "D"); // priority queue is empty return _queue.data[_queue.head]; } /// @notice Remove the first unprocessed priority operation from the queue /// @return priorityOperation that was popped from the priority queue function popFront(Queue storage _queue) internal returns (PriorityOperation memory priorityOperation) { require(!_queue.isEmpty(), "s"); // priority queue is empty // Save value into the stack to avoid double reading from the storage uint256 head = _queue.head; priorityOperation = _queue.data[head]; delete _queue.data[head]; _queue.head = head + 1; } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./PairingsBn254.sol"; library TranscriptLib { // flip 0xe000000000000000000000000000000000000000000000000000000000000000; uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; uint32 constant DST_0 = 0; uint32 constant DST_1 = 1; uint32 constant DST_CHALLENGE = 2; struct Transcript { bytes32 state_0; bytes32 state_1; uint32 challenge_counter; } function new_transcript() internal pure returns (Transcript memory t) { t.state_0 = bytes32(0); t.state_1 = bytes32(0); t.challenge_counter = 0; } function update_with_u256(Transcript memory self, uint256 value) internal pure { bytes32 old_state_0 = self.state_0; self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value)); self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value)); } function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure { update_with_u256(self, value.value); } function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure { update_with_u256(self, p.X); update_with_u256(self, p.Y); } function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) { bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter)); self.challenge_counter += 1; challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK}); } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./libraries/PairingsBn254.sol"; import "./libraries/TranscriptLib.sol"; import "../common/libraries/UncheckedMath.sol"; uint256 constant STATE_WIDTH = 4; uint256 constant NUM_G2_ELS = 2; struct VerificationKey { uint256 domain_size; uint256 num_inputs; PairingsBn254.Fr omega; PairingsBn254.G1Point[2] gate_selectors_commitments; PairingsBn254.G1Point[8] gate_setup_commitments; PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments; PairingsBn254.G1Point lookup_selector_commitment; PairingsBn254.G1Point[4] lookup_tables_commitments; PairingsBn254.G1Point lookup_table_type_commitment; PairingsBn254.Fr[STATE_WIDTH - 1] non_residues; PairingsBn254.G2Point[NUM_G2_ELS] g2_elements; } contract Plonk4VerifierWithAccessToDNext { using PairingsBn254 for PairingsBn254.G1Point; using PairingsBn254 for PairingsBn254.G2Point; using PairingsBn254 for PairingsBn254.Fr; using TranscriptLib for TranscriptLib.Transcript; using UncheckedMath for uint256; struct Proof { uint256[] input_values; // commitments PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments; PairingsBn254.G1Point copy_permutation_grand_product_commitment; PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments; // openings PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z; PairingsBn254.Fr[1] state_polys_openings_at_z_omega; PairingsBn254.Fr[1] gate_selectors_openings_at_z; PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_polys_openings_at_z; PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega; PairingsBn254.Fr quotient_poly_opening_at_z; PairingsBn254.Fr linearization_poly_opening_at_z; // lookup commitments PairingsBn254.G1Point lookup_s_poly_commitment; PairingsBn254.G1Point lookup_grand_product_commitment; // lookup openings PairingsBn254.Fr lookup_s_poly_opening_at_z_omega; PairingsBn254.Fr lookup_grand_product_opening_at_z_omega; PairingsBn254.Fr lookup_t_poly_opening_at_z; PairingsBn254.Fr lookup_t_poly_opening_at_z_omega; PairingsBn254.Fr lookup_selector_poly_opening_at_z; PairingsBn254.Fr lookup_table_type_poly_opening_at_z; PairingsBn254.G1Point opening_proof_at_z; PairingsBn254.G1Point opening_proof_at_z_omega; } struct PartialVerifierState { PairingsBn254.Fr zero; PairingsBn254.Fr alpha; PairingsBn254.Fr beta; PairingsBn254.Fr gamma; PairingsBn254.Fr[9] alpha_values; PairingsBn254.Fr eta; PairingsBn254.Fr beta_lookup; PairingsBn254.Fr gamma_lookup; PairingsBn254.Fr beta_plus_one; PairingsBn254.Fr beta_gamma; PairingsBn254.Fr v; PairingsBn254.Fr u; PairingsBn254.Fr z; PairingsBn254.Fr z_omega; PairingsBn254.Fr z_minus_last_omega; PairingsBn254.Fr l_0_at_z; PairingsBn254.Fr l_n_minus_one_at_z; PairingsBn254.Fr t; PairingsBn254.G1Point tp; } function evaluate_l0_at_point(uint256 domain_size, PairingsBn254.Fr memory at) internal view returns (PairingsBn254.Fr memory num) { PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); PairingsBn254.Fr memory size_fe = PairingsBn254.new_fr(domain_size); PairingsBn254.Fr memory den = at.copy(); den.sub_assign(one); den.mul_assign(size_fe); den = den.inverse(); num = at.pow(domain_size); num.sub_assign(one); num.mul_assign(den); } function evaluate_lagrange_poly_out_of_domain( uint256 poly_num, uint256 domain_size, PairingsBn254.Fr memory omega, PairingsBn254.Fr memory at ) internal view returns (PairingsBn254.Fr memory res) { // (omega^i / N) / (X - omega^i) * (X^N - 1) require(poly_num < domain_size); PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); PairingsBn254.Fr memory omega_power = omega.pow(poly_num); res = at.pow(domain_size); res.sub_assign(one); require(res.value != 0); // Vanishing polynomial cannot be zero at point `at` res.mul_assign(omega_power); PairingsBn254.Fr memory den = PairingsBn254.copy(at); den.sub_assign(omega_power); den.mul_assign(PairingsBn254.new_fr(domain_size)); den = den.inverse(); res.mul_assign(den); } function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at) internal view returns (PairingsBn254.Fr memory res) { res = at.pow(domain_size); res.sub_assign(PairingsBn254.new_fr(1)); } function initialize_transcript(Proof memory proof, VerificationKey memory vk) internal pure returns (PartialVerifierState memory state) { TranscriptLib.Transcript memory transcript = TranscriptLib.new_transcript(); for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) { transcript.update_with_u256(proof.input_values[i]); } for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { transcript.update_with_g1(proof.state_polys_commitments[i]); } state.eta = transcript.get_challenge(); transcript.update_with_g1(proof.lookup_s_poly_commitment); state.beta = transcript.get_challenge(); state.gamma = transcript.get_challenge(); transcript.update_with_g1(proof.copy_permutation_grand_product_commitment); state.beta_lookup = transcript.get_challenge(); state.gamma_lookup = transcript.get_challenge(); transcript.update_with_g1(proof.lookup_grand_product_commitment); state.alpha = transcript.get_challenge(); for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { transcript.update_with_g1(proof.quotient_poly_parts_commitments[i]); } state.z = transcript.get_challenge(); transcript.update_with_fr(proof.quotient_poly_opening_at_z); for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) { transcript.update_with_fr(proof.state_polys_openings_at_z[i]); } for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) { transcript.update_with_fr(proof.state_polys_openings_at_z_omega[i]); } for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) { transcript.update_with_fr(proof.gate_selectors_openings_at_z[i]); } for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { transcript.update_with_fr(proof.copy_permutation_polys_openings_at_z[i]); } state.z_omega = state.z.copy(); state.z_omega.mul_assign(vk.omega); transcript.update_with_fr(proof.copy_permutation_grand_product_opening_at_z_omega); transcript.update_with_fr(proof.lookup_t_poly_opening_at_z); transcript.update_with_fr(proof.lookup_selector_poly_opening_at_z); transcript.update_with_fr(proof.lookup_table_type_poly_opening_at_z); transcript.update_with_fr(proof.lookup_s_poly_opening_at_z_omega); transcript.update_with_fr(proof.lookup_grand_product_opening_at_z_omega); transcript.update_with_fr(proof.lookup_t_poly_opening_at_z_omega); transcript.update_with_fr(proof.linearization_poly_opening_at_z); state.v = transcript.get_challenge(); transcript.update_with_g1(proof.opening_proof_at_z); transcript.update_with_g1(proof.opening_proof_at_z_omega); state.u = transcript.get_challenge(); } // compute some powers of challenge alpha([alpha^1, .. alpha^8]) function compute_powers_of_alpha(PartialVerifierState memory state) public pure { require(state.alpha.value != 0); state.alpha_values[0] = PairingsBn254.new_fr(1); state.alpha_values[1] = state.alpha.copy(); PairingsBn254.Fr memory current_alpha = state.alpha.copy(); for (uint256 i = 2; i < state.alpha_values.length; i = i.uncheckedInc()) { current_alpha.mul_assign(state.alpha); state.alpha_values[i] = current_alpha.copy(); } } function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) { // we initialize all challenges beforehand, we can draw each challenge in its own place PartialVerifierState memory state = initialize_transcript(proof, vk); if (verify_quotient_evaluation(vk, proof, state) == false) { return false; } require(proof.state_polys_openings_at_z_omega.length == 1); PairingsBn254.G1Point memory quotient_result = proof.quotient_poly_parts_commitments[0].copy_g1(); { // block scope PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size); PairingsBn254.Fr memory current_z = z_in_domain_size.copy(); PairingsBn254.G1Point memory tp; // start from i =1 for (uint256 i = 1; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { tp = proof.quotient_poly_parts_commitments[i].copy_g1(); tp.point_mul_assign(current_z); quotient_result.point_add_assign(tp); current_z.mul_assign(z_in_domain_size); } } Queries memory queries = prepare_queries(vk, proof, state); queries.commitments_at_z[0] = quotient_result; queries.values_at_z[0] = proof.quotient_poly_opening_at_z; queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state); queries.values_at_z[1] = proof.linearization_poly_opening_at_z; require(queries.commitments_at_z.length == queries.values_at_z.length); PairingsBn254.G1Point memory aggregated_commitment_at_z = queries.commitments_at_z[0]; PairingsBn254.Fr memory aggregated_opening_at_z = queries.values_at_z[0]; PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1); PairingsBn254.G1Point memory scaled; for (uint256 i = 1; i < queries.commitments_at_z.length; i = i.uncheckedInc()) { aggregation_challenge.mul_assign(state.v); scaled = queries.commitments_at_z[i].point_mul(aggregation_challenge); aggregated_commitment_at_z.point_add_assign(scaled); state.t = queries.values_at_z[i]; state.t.mul_assign(aggregation_challenge); aggregated_opening_at_z.add_assign(state.t); } aggregation_challenge.mul_assign(state.v); PairingsBn254.G1Point memory aggregated_commitment_at_z_omega = queries.commitments_at_z_omega[0].point_mul( aggregation_challenge ); PairingsBn254.Fr memory aggregated_opening_at_z_omega = queries.values_at_z_omega[0]; aggregated_opening_at_z_omega.mul_assign(aggregation_challenge); for (uint256 i = 1; i < queries.commitments_at_z_omega.length; i = i.uncheckedInc()) { aggregation_challenge.mul_assign(state.v); scaled = queries.commitments_at_z_omega[i].point_mul(aggregation_challenge); aggregated_commitment_at_z_omega.point_add_assign(scaled); state.t = queries.values_at_z_omega[i]; state.t.mul_assign(aggregation_challenge); aggregated_opening_at_z_omega.add_assign(state.t); } return final_pairing( vk.g2_elements, proof, state, aggregated_commitment_at_z, aggregated_commitment_at_z_omega, aggregated_opening_at_z, aggregated_opening_at_z_omega ); } function verify_quotient_evaluation( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) internal view returns (bool) { uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs); for (uint256 i = 0; i < lagrange_poly_numbers.length; i = i.uncheckedInc()) { lagrange_poly_numbers[i] = i; } require(vk.num_inputs > 0); PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0); for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) { state.t = evaluate_lagrange_poly_out_of_domain(i, vk.domain_size, vk.omega, state.z); state.t.mul_assign(PairingsBn254.new_fr(proof.input_values[i])); inputs_term.add_assign(state.t); } inputs_term.mul_assign(proof.gate_selectors_openings_at_z[0]); PairingsBn254.Fr memory result = proof.linearization_poly_opening_at_z.copy(); result.add_assign(inputs_term); // compute powers of alpha compute_powers_of_alpha(state); PairingsBn254.Fr memory factor = state.alpha_values[4].copy(); factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega); // - alpha_0 * (a + perm(z) * beta + gamma)*()*(d + gamma) * z(z*omega) require(proof.copy_permutation_polys_openings_at_z.length == STATE_WIDTH - 1); PairingsBn254.Fr memory t; // TMP; for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { t = proof.copy_permutation_polys_openings_at_z[i].copy(); t.mul_assign(state.beta); t.add_assign(proof.state_polys_openings_at_z[i]); t.add_assign(state.gamma); factor.mul_assign(t); } t = proof.state_polys_openings_at_z[3].copy(); t.add_assign(state.gamma); factor.mul_assign(t); result.sub_assign(factor); // - L_0(z) * alpha_1 PairingsBn254.Fr memory l_0_at_z = evaluate_l0_at_point(vk.domain_size, state.z); l_0_at_z.mul_assign(state.alpha_values[4 + 1]); result.sub_assign(l_0_at_z); PairingsBn254.Fr memory lookup_quotient_contrib = lookup_quotient_contribution(vk, proof, state); result.add_assign(lookup_quotient_contrib); PairingsBn254.Fr memory lhs = proof.quotient_poly_opening_at_z.copy(); lhs.mul_assign(evaluate_vanishing(vk.domain_size, state.z)); return lhs.value == result.value; } function lookup_quotient_contribution( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) internal view returns (PairingsBn254.Fr memory result) { PairingsBn254.Fr memory t; PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); state.beta_plus_one = state.beta_lookup.copy(); state.beta_plus_one.add_assign(one); state.beta_gamma = state.beta_plus_one.copy(); state.beta_gamma.mul_assign(state.gamma_lookup); // (s'*beta + gamma)*(zw')*alpha t = proof.lookup_s_poly_opening_at_z_omega.copy(); t.mul_assign(state.beta_lookup); t.add_assign(state.beta_gamma); t.mul_assign(proof.lookup_grand_product_opening_at_z_omega); t.mul_assign(state.alpha_values[6]); // (z - omega^{n-1}) for this part PairingsBn254.Fr memory last_omega = vk.omega.pow(vk.domain_size - 1); state.z_minus_last_omega = state.z.copy(); state.z_minus_last_omega.sub_assign(last_omega); t.mul_assign(state.z_minus_last_omega); result.add_assign(t); // - alpha_1 * L_{0}(z) state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z); t = state.l_0_at_z.copy(); t.mul_assign(state.alpha_values[6 + 1]); result.sub_assign(t); // - alpha_2 * beta_gamma_powered L_{n-1}(z) PairingsBn254.Fr memory beta_gamma_powered = state.beta_gamma.pow(vk.domain_size - 1); state.l_n_minus_one_at_z = evaluate_lagrange_poly_out_of_domain( vk.domain_size - 1, vk.domain_size, vk.omega, state.z ); t = state.l_n_minus_one_at_z.copy(); t.mul_assign(beta_gamma_powered); t.mul_assign(state.alpha_values[6 + 2]); result.sub_assign(t); } function aggregated_linearization_commitment( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) internal view returns (PairingsBn254.G1Point memory result) { // qMain*(Q_a * A + Q_b * B + Q_c * C + Q_d * D + Q_m * A*B + Q_const + Q_dNext * D_next) result = PairingsBn254.new_g1(0, 0); // Q_a * A PairingsBn254.G1Point memory scaled = vk.gate_setup_commitments[0].point_mul( proof.state_polys_openings_at_z[0] ); result.point_add_assign(scaled); // Q_b * B scaled = vk.gate_setup_commitments[1].point_mul(proof.state_polys_openings_at_z[1]); result.point_add_assign(scaled); // Q_c * C scaled = vk.gate_setup_commitments[2].point_mul(proof.state_polys_openings_at_z[2]); result.point_add_assign(scaled); // Q_d * D scaled = vk.gate_setup_commitments[3].point_mul(proof.state_polys_openings_at_z[3]); result.point_add_assign(scaled); // Q_m* A*B or Q_ab*A*B PairingsBn254.Fr memory t = proof.state_polys_openings_at_z[0].copy(); t.mul_assign(proof.state_polys_openings_at_z[1]); scaled = vk.gate_setup_commitments[4].point_mul(t); result.point_add_assign(scaled); // Q_AC* A*C t = proof.state_polys_openings_at_z[0].copy(); t.mul_assign(proof.state_polys_openings_at_z[2]); scaled = vk.gate_setup_commitments[5].point_mul(t); result.point_add_assign(scaled); // Q_const result.point_add_assign(vk.gate_setup_commitments[6]); // Q_dNext * D_next scaled = vk.gate_setup_commitments[7].point_mul(proof.state_polys_openings_at_z_omega[0]); result.point_add_assign(scaled); result.point_mul_assign(proof.gate_selectors_openings_at_z[0]); PairingsBn254.G1Point memory rescue_custom_gate_linearization_contrib = rescue_custom_gate_linearization_contribution( vk, proof, state ); result.point_add_assign(rescue_custom_gate_linearization_contrib); require(vk.non_residues.length == STATE_WIDTH - 1); PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); PairingsBn254.Fr memory factor = state.alpha_values[4].copy(); for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; ) { t = state.z.copy(); if (i == 0) { t.mul_assign(one); } else { t.mul_assign(vk.non_residues[i - 1]); } t.mul_assign(state.beta); t.add_assign(state.gamma); t.add_assign(proof.state_polys_openings_at_z[i]); factor.mul_assign(t); unchecked { ++i; } } scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor); result.point_add_assign(scaled); // - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X) factor = state.alpha_values[4].copy(); factor.mul_assign(state.beta); factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega); for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { t = proof.copy_permutation_polys_openings_at_z[i].copy(); t.mul_assign(state.beta); t.add_assign(state.gamma); t.add_assign(proof.state_polys_openings_at_z[i]); factor.mul_assign(t); } scaled = vk.permutation_commitments[3].point_mul(factor); result.point_sub_assign(scaled); // + L_0(z) * Z(x) state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z); require(state.l_0_at_z.value != 0); factor = state.l_0_at_z.copy(); factor.mul_assign(state.alpha_values[4 + 1]); scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor); result.point_add_assign(scaled); PairingsBn254.G1Point memory lookup_linearization_contrib = lookup_linearization_contribution(proof, state); result.point_add_assign(lookup_linearization_contrib); } function rescue_custom_gate_linearization_contribution( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) public view returns (PairingsBn254.G1Point memory result) { PairingsBn254.Fr memory t; PairingsBn254.Fr memory intermediate_result; // a^2 - b = 0 t = proof.state_polys_openings_at_z[0].copy(); t.mul_assign(t); t.sub_assign(proof.state_polys_openings_at_z[1]); // t.mul_assign(challenge1); t.mul_assign(state.alpha_values[1]); intermediate_result.add_assign(t); // b^2 - c = 0 t = proof.state_polys_openings_at_z[1].copy(); t.mul_assign(t); t.sub_assign(proof.state_polys_openings_at_z[2]); t.mul_assign(state.alpha_values[1 + 1]); intermediate_result.add_assign(t); // c*a - d = 0; t = proof.state_polys_openings_at_z[2].copy(); t.mul_assign(proof.state_polys_openings_at_z[0]); t.sub_assign(proof.state_polys_openings_at_z[3]); t.mul_assign(state.alpha_values[1 + 2]); intermediate_result.add_assign(t); result = vk.gate_selectors_commitments[1].point_mul(intermediate_result); } function lookup_linearization_contribution(Proof memory proof, PartialVerifierState memory state) internal view returns (PairingsBn254.G1Point memory result) { PairingsBn254.Fr memory zero = PairingsBn254.new_fr(0); PairingsBn254.Fr memory t; PairingsBn254.Fr memory factor; // s(x) from the Z(x*omega)*(\\gamma*(1 + \\beta) + s(x) + \\beta * s(x*omega))) factor = proof.lookup_grand_product_opening_at_z_omega.copy(); factor.mul_assign(state.alpha_values[6]); factor.mul_assign(state.z_minus_last_omega); PairingsBn254.G1Point memory scaled = proof.lookup_s_poly_commitment.point_mul(factor); result.point_add_assign(scaled); // Z(x) from - alpha_0 * Z(x) * (\\beta + 1) * (\\gamma + f(x)) * (\\gamma(1 + \\beta) + t(x) + \\beta * t(x*omega)) // + alpha_1 * Z(x) * L_{0}(z) + alpha_2 * Z(x) * L_{n-1}(z) // accumulate coefficient factor = proof.lookup_t_poly_opening_at_z_omega.copy(); factor.mul_assign(state.beta_lookup); factor.add_assign(proof.lookup_t_poly_opening_at_z); factor.add_assign(state.beta_gamma); // (\\gamma + f(x)) PairingsBn254.Fr memory f_reconstructed; PairingsBn254.Fr memory current = PairingsBn254.new_fr(1); PairingsBn254.Fr memory tmp0; for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { tmp0 = proof.state_polys_openings_at_z[i].copy(); tmp0.mul_assign(current); f_reconstructed.add_assign(tmp0); current.mul_assign(state.eta); } // add type of table t = proof.lookup_table_type_poly_opening_at_z.copy(); t.mul_assign(current); f_reconstructed.add_assign(t); f_reconstructed.mul_assign(proof.lookup_selector_poly_opening_at_z); f_reconstructed.add_assign(state.gamma_lookup); // end of (\\gamma + f(x)) part factor.mul_assign(f_reconstructed); factor.mul_assign(state.beta_plus_one); t = zero.copy(); t.sub_assign(factor); factor = t; factor.mul_assign(state.alpha_values[6]); // Multiply by (z - omega^{n-1}) factor.mul_assign(state.z_minus_last_omega); // L_{0}(z) in front of Z(x) t = state.l_0_at_z.copy(); t.mul_assign(state.alpha_values[6 + 1]); factor.add_assign(t); // L_{n-1}(z) in front of Z(x) t = state.l_n_minus_one_at_z.copy(); t.mul_assign(state.alpha_values[6 + 2]); factor.add_assign(t); scaled = proof.lookup_grand_product_commitment.point_mul(factor); result.point_add_assign(scaled); } struct Queries { PairingsBn254.G1Point[13] commitments_at_z; PairingsBn254.Fr[13] values_at_z; PairingsBn254.G1Point[6] commitments_at_z_omega; PairingsBn254.Fr[6] values_at_z_omega; } function prepare_queries( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) public view returns (Queries memory queries) { // we set first two items in calee side so start idx from 2 uint256 idx = 2; for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { queries.commitments_at_z[idx] = proof.state_polys_commitments[i]; queries.values_at_z[idx] = proof.state_polys_openings_at_z[i]; idx = idx.uncheckedInc(); } require(proof.gate_selectors_openings_at_z.length == 1); queries.commitments_at_z[idx] = vk.gate_selectors_commitments[0]; queries.values_at_z[idx] = proof.gate_selectors_openings_at_z[0]; idx = idx.uncheckedInc(); for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { queries.commitments_at_z[idx] = vk.permutation_commitments[i]; queries.values_at_z[idx] = proof.copy_permutation_polys_openings_at_z[i]; idx = idx.uncheckedInc(); } queries.commitments_at_z_omega[0] = proof.copy_permutation_grand_product_commitment; queries.commitments_at_z_omega[1] = proof.state_polys_commitments[STATE_WIDTH - 1]; queries.values_at_z_omega[0] = proof.copy_permutation_grand_product_opening_at_z_omega; queries.values_at_z_omega[1] = proof.state_polys_openings_at_z_omega[0]; PairingsBn254.G1Point memory lookup_t_poly_commitment_aggregated = vk.lookup_tables_commitments[0]; PairingsBn254.Fr memory current_eta = state.eta.copy(); for (uint256 i = 1; i < vk.lookup_tables_commitments.length; i = i.uncheckedInc()) { state.tp = vk.lookup_tables_commitments[i].point_mul(current_eta); lookup_t_poly_commitment_aggregated.point_add_assign(state.tp); current_eta.mul_assign(state.eta); } queries.commitments_at_z[idx] = lookup_t_poly_commitment_aggregated; queries.values_at_z[idx] = proof.lookup_t_poly_opening_at_z; idx = idx.uncheckedInc(); queries.commitments_at_z[idx] = vk.lookup_selector_commitment; queries.values_at_z[idx] = proof.lookup_selector_poly_opening_at_z; idx = idx.uncheckedInc(); queries.commitments_at_z[idx] = vk.lookup_table_type_commitment; queries.values_at_z[idx] = proof.lookup_table_type_poly_opening_at_z; queries.commitments_at_z_omega[2] = proof.lookup_s_poly_commitment; queries.values_at_z_omega[2] = proof.lookup_s_poly_opening_at_z_omega; queries.commitments_at_z_omega[3] = proof.lookup_grand_product_commitment; queries.values_at_z_omega[3] = proof.lookup_grand_product_opening_at_z_omega; queries.commitments_at_z_omega[4] = lookup_t_poly_commitment_aggregated; queries.values_at_z_omega[4] = proof.lookup_t_poly_opening_at_z_omega; } function final_pairing( // VerificationKey memory vk, PairingsBn254.G2Point[NUM_G2_ELS] memory g2_elements, Proof memory proof, PartialVerifierState memory state, PairingsBn254.G1Point memory aggregated_commitment_at_z, PairingsBn254.G1Point memory aggregated_commitment_at_z_omega, PairingsBn254.Fr memory aggregated_opening_at_z, PairingsBn254.Fr memory aggregated_opening_at_z_omega ) internal view returns (bool) { // q(x) = f(x) - f(z) / (x - z) // q(x) * (x-z) = f(x) - f(z) // f(x) PairingsBn254.G1Point memory pair_with_generator = aggregated_commitment_at_z.copy_g1(); aggregated_commitment_at_z_omega.point_mul_assign(state.u); pair_with_generator.point_add_assign(aggregated_commitment_at_z_omega); // - f(z)*g PairingsBn254.Fr memory aggregated_value = aggregated_opening_at_z_omega.copy(); aggregated_value.mul_assign(state.u); aggregated_value.add_assign(aggregated_opening_at_z); PairingsBn254.G1Point memory tp = PairingsBn254.P1().point_mul(aggregated_value); pair_with_generator.point_sub_assign(tp); // +z * q(x) tp = proof.opening_proof_at_z.point_mul(state.z); PairingsBn254.Fr memory t = state.z_omega.copy(); t.mul_assign(state.u); PairingsBn254.G1Point memory t1 = proof.opening_proof_at_z_omega.point_mul(t); tp.point_add_assign(t1); pair_with_generator.point_add_assign(tp); // rhs PairingsBn254.G1Point memory pair_with_x = proof.opening_proof_at_z_omega.point_mul(state.u); pair_with_x.point_add_assign(proof.opening_proof_at_z); pair_with_x.negate(); // Pairing precompile expects points to be in a `i*x[1] + x[0]` form instead of `x[0] + i*x[1]` // so we handle it in code generation step PairingsBn254.G2Point memory first_g2 = g2_elements[0]; PairingsBn254.G2Point memory second_g2 = g2_elements[1]; return PairingsBn254.pairingProd2(pair_with_generator, first_g2, pair_with_x, second_g2); } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./Verifier.sol"; import "../common/interfaces/IAllowList.sol"; import "./libraries/PriorityQueue.sol"; /// @notice Indicates whether an upgrade is initiated and if yes what type /// @param None Upgrade is NOT initiated /// @param Transparent Fully transparent upgrade is initiated, upgrade data is publicly known /// @param Shadow Shadow upgrade is initiated, upgrade data is hidden enum UpgradeState { None, Transparent, Shadow } /// @dev Logically separated part of the storage structure, which is responsible for everything related to proxy upgrades and diamond cuts /// @param proposedUpgradeHash The hash of the current upgrade proposal, zero if there is no active proposal /// @param state Indicates whether an upgrade is initiated and if yes what type /// @param securityCouncil Address which has the permission to approve instant upgrades (expected to be a Gnosis multisig) /// @param approvedBySecurityCouncil Indicates whether the security council has approved the upgrade /// @param proposedUpgradeTimestamp The timestamp when the upgrade was proposed, zero if there are no active proposals /// @param currentProposalId The serial number of proposed upgrades, increments when proposing a new one struct UpgradeStorage { bytes32 proposedUpgradeHash; UpgradeState state; address securityCouncil; bool approvedBySecurityCouncil; uint40 proposedUpgradeTimestamp; uint40 currentProposalId; } /// @dev The log passed from L2 /// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter. All other values are not used but are reserved for the future /// @param isService A boolean flag that is part of the log along with `key`, `value`, and `sender` address. /// This field is required formally but does not have any special meaning. /// @param txNumberInBlock The L2 transaction number in a block, in which the log was sent /// @param sender The L2 address which sent the log /// @param key The 32 bytes of information that was sent in the log /// @param value The 32 bytes of information that was sent in the log // Both `key` and `value` are arbitrary 32-bytes selected by the log sender struct L2Log { uint8 l2ShardId; bool isService; uint16 txNumberInBlock; address sender; bytes32 key; bytes32 value; } /// @dev An arbitrary length message passed from L2 /// @notice Under the hood it is `L2Log` sent from the special system L2 contract /// @param txNumberInBlock The L2 transaction number in a block, in which the message was sent /// @param sender The address of the L2 account from which the message was passed /// @param data An arbitrary length message struct L2Message { uint16 txNumberInBlock; address sender; bytes data; } /// @notice Part of the configuration parameters of ZKP circuits struct VerifierParams { bytes32 recursionNodeLevelVkHash; bytes32 recursionLeafLevelVkHash; bytes32 recursionCircuitsSetVksHash; } /// @dev storing all storage variables for zkSync facets /// NOTE: It is used in a proxy, so it is possible to add new variables to the end /// but NOT to modify already existing variables or change their order. /// NOTE: variables prefixed with '__DEPRECATED_' are deprecated and shouldn't be used. /// Their presence is maintained for compatibility and to prevent storage collision. struct AppStorage { /// @dev Storage of variables needed for deprecated diamond cut facet uint256[7] __DEPRECATED_diamondCutStorage; /// @notice Address which will exercise governance over the network i.e. change validator set, conduct upgrades address governor; /// @notice Address that the governor proposed as one that will replace it address pendingGovernor; /// @notice List of permitted validators mapping(address => bool) validators; /// @dev Verifier contract. Used to verify aggregated proof for blocks Verifier verifier; /// @notice Total number of executed blocks i.e. blocks[totalBlocksExecuted] points at the latest executed block (block 0 is genesis) uint256 totalBlocksExecuted; /// @notice Total number of proved blocks i.e. blocks[totalBlocksProved] points at the latest proved block uint256 totalBlocksVerified; /// @notice Total number of committed blocks i.e. blocks[totalBlocksCommitted] points at the latest committed block uint256 totalBlocksCommitted; /// @dev Stored hashed StoredBlock for block number mapping(uint256 => bytes32) storedBlockHashes; /// @dev Stored root hashes of L2 -> L1 logs mapping(uint256 => bytes32) l2LogsRootHashes; /// @dev Container that stores transactions requested from L1 PriorityQueue.Queue priorityQueue; /// @dev The smart contract that manages the list with permission to call contract functions IAllowList allowList; /// @notice Part of the configuration parameters of ZKP circuits. Used as an input for the verifier smart contract VerifierParams verifierParams; /// @notice Bytecode hash of bootloader program. /// @dev Used as an input to zkp-circuit. bytes32 l2BootloaderBytecodeHash; /// @notice Bytecode hash of default account (bytecode for EOA). /// @dev Used as an input to zkp-circuit. bytes32 l2DefaultAccountBytecodeHash; /// @dev Indicates that the porter may be touched on L2 transactions. /// @dev Used as an input to zkp-circuit. bool zkPorterIsAvailable; /// @dev The maximum number of the L2 gas that a user can request for L1 -> L2 transactions /// @dev This is the maximum number of L2 gas that is available for the "body" of the transaction, i.e. /// without overhead for proving the block. uint256 priorityTxMaxGasLimit; /// @dev Storage of variables needed for upgrade facet UpgradeStorage upgrades; /// @dev A mapping L2 block number => message number => flag. /// @dev The L2 -> L1 log is sent for every withdrawal, so this mapping is serving as /// a flag to indicate that the message was already processed. /// @dev Used to indicate that eth withdrawal was already processed mapping(uint256 => mapping(uint256 => bool)) isEthWithdrawalFinalized; /// @dev The most recent withdrawal time and amount reset uint256 __DEPRECATED_lastWithdrawalLimitReset; /// @dev The accumulated withdrawn amount during the withdrawal limit window uint256 __DEPRECATED_withdrawnAmountInWindow; /// @dev A mapping user address => the total deposited amount by the user mapping(address => uint256) totalDepositedAmountPerUser; /// @dev Stores the protocol version. Note, that the protocol version may not only encompass changes to the /// smart contracts, but also to the node behavior. uint256 protocolVersion; /// @dev Hash of the system contract upgrade transaction. If 0, then no upgrade transaction needs to be done. bytes32 l2SystemContractsUpgradeTxHash; /// @dev Block number where the upgrade transaction has happened. If 0, then no upgrade transaction has happened yet. uint256 l2SystemContractsUpgradeBlockNumber; } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./Plonk4VerifierWithAccessToDNext.sol"; import "../common/libraries/UncheckedMath.sol"; contract Verifier is Plonk4VerifierWithAccessToDNext { using UncheckedMath for uint256; function get_verification_key() public pure returns (VerificationKey memory vk) { vk.num_inputs = 1; vk.domain_size = 67108864; vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97); // coefficients vk.gate_setup_commitments[0] = PairingsBn254.new_g1( 0x14c289d746e37aa82ec428491881c4732766492a8bc2e8e3cca2000a40c0ea27, 0x2f617a7eb9808ad9843d1e080b7cfbf99d61bb1b02076c905f31adb12731bc41 ); vk.gate_setup_commitments[1] = PairingsBn254.new_g1( 0x210b5cc8e6a85d63b65b701b8fb5ad24ff9c41f923432de17fe4ebae04526a8c, 0x05c10ab17ea731b2b87fb890fa5b10bd3d6832917a616b807a9b640888ebc731 ); vk.gate_setup_commitments[2] = PairingsBn254.new_g1( 0x29d4d14adcfe67a2ac690d6369db6b75e82d8ab3124bc4fa1dd145f41ca6949c, 0x004f6cd229373f1c1f735ccf49aef6a5c32025bc36c3328596dd0db7d87bef67 ); vk.gate_setup_commitments[3] = PairingsBn254.new_g1( 0x06d15382e8cabae9f98374a9fbdadd424f48e24da7e4c65bf710fd7d7d59a05a, 0x22e438ad5c51673879ce17073a3d2d29327a97dc3ce61c4f88540e00087695f6 ); vk.gate_setup_commitments[4] = PairingsBn254.new_g1( 0x274a668dfc485cf192d0086f214146d9e02b3040a5a586df344c53c16a87882b, 0x15f5bb7ad01f162b70fc77c8ea456d67d15a6ce98acbbfd521222810f8ec0a66 ); vk.gate_setup_commitments[5] = PairingsBn254.new_g1( 0x0ba53bf4fb0446927857e33978d02abf45948fc68f4091394ae0827a22cf1e47, 0x0720d818751ce5b3f11c716e925f60df4679ea90bed516499bdec066f5ff108f ); vk.gate_setup_commitments[6] = PairingsBn254.new_g1( 0x2e986ba2ea495e5ec6af532980b1dc567f1430bfa82f8de07c12fc097c0e0483, 0x1555d189f6164e82d78de1b8313c2e923e616b3c8ed0e350c3b61c94516d0b58 ); vk.gate_setup_commitments[7] = PairingsBn254.new_g1( 0x0925959592604ca73c917f9b2e029aa2563c318ddcc5ca29c11badb7b880127b, 0x2b4a430fcb2fa7d6d67d6c358e01cf0524c7df7e1e56442f65b39bc1a1052367 ); // gate selectors vk.gate_selectors_commitments[0] = PairingsBn254.new_g1( 0x28f2a0a95af79ba67e9dd1986bd3190199f661b710a693fc82fb395c126edcbd, 0x0db75db5de5192d1ba1c24710fc00da16fa8029ac7fe82d855674dcd6d090e05 ); vk.gate_selectors_commitments[1] = PairingsBn254.new_g1( 0x143471a174dfcb2d9cb5ae621e519387bcc93c9dcfc011160b2f5c5f88e32cbe, 0x2a0194c0224c3d964223a96c4c99e015719bc879125aa0df3f0715d154e71a31 ); // permutation vk.permutation_commitments[0] = PairingsBn254.new_g1( 0x1423fa82e00ba22c280181afb12c56eea541933eeb5ec39119b0365b6beab4b9, 0x0efdcd3423a38f5e2ecf8c7e4fd46f13189f8fed392ad9d8d393e8ba568b06e4 ); vk.permutation_commitments[1] = PairingsBn254.new_g1( 0x0e9b5b12c1090d62224e64aa1696c009aa59a9c3eec458e781fae773e1f4eca5, 0x1fe3df508c7e9750eb37d9cae5e7437ad11a21fa36530ff821b407b165a79a55 ); vk.permutation_commitments[2] = PairingsBn254.new_g1( 0x25d1a714bd1e258f196e38d6b2826153382c2d04b870d0b7ec250296005129ae, 0x0883a121b41ca7beaa9de97ecf4417e62aa2eeb9434f24ddacbfed57cbf016a8 ); vk.permutation_commitments[3] = PairingsBn254.new_g1( 0x2f3ede68e854a6b3b14589851cf077a606e2aeb3205c43cc579b7abae39d8f58, 0x178ccd4b1f78fd79ee248e376b6fc8297d5450900d1e15e8c03e3ed2c171ac8c ); // lookup table commitments vk.lookup_selector_commitment = PairingsBn254.new_g1( 0x1f814e2d87c332e964eeef94ec695eef9d2caaac58b682a43da5107693b06f30, 0x196d56fb01907e66af9303886fd95328d398e5b2b72906882a9d12c1718e2ee2 ); vk.lookup_tables_commitments[0] = PairingsBn254.new_g1( 0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c, 0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b ); vk.lookup_tables_commitments[1] = PairingsBn254.new_g1( 0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9, 0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca ); vk.lookup_tables_commitments[2] = PairingsBn254.new_g1( 0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44, 0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765 ); vk.lookup_tables_commitments[3] = PairingsBn254.new_g1( 0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e, 0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e ); vk.lookup_table_type_commitment = PairingsBn254.new_g1( 0x2f85df2d6249ccbcc11b91727333cc800459de6ee274f29c657c8d56f6f01563, 0x088e1df178c47116a69c3c8f6d0c5feb530e2a72493694a623b1cceb7d44a76c ); // non residues vk.non_residues[0] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000005); vk.non_residues[1] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000007); vk.non_residues[2] = PairingsBn254.new_fr(0x000000000000000000000000000000000000000000000000000000000000000a); // g2 elements vk.g2_elements[0] = PairingsBn254.new_g2( [ 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed ], [ 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa ] ); vk.g2_elements[1] = PairingsBn254.new_g2( [ 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1, 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 ], [ 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4, 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 ] ); } function deserialize_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof) internal pure returns (Proof memory proof) { require(serialized_proof.length == 44); proof.input_values = new uint256[](public_inputs.length); for (uint256 i = 0; i < public_inputs.length; i = i.uncheckedInc()) { proof.input_values[i] = public_inputs[i]; } uint256 j; for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { proof.state_polys_commitments[i] = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); } proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); proof.lookup_s_poly_commitment = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); proof.lookup_grand_product_commitment = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { proof.quotient_poly_parts_commitments[i] = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); } for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) { proof.state_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); } for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) { proof.state_polys_openings_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); } for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) { proof.gate_selectors_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); } for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { proof.copy_permutation_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); } proof.copy_permutation_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_s_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_t_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_t_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_selector_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_table_type_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.quotient_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.linearization_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.opening_proof_at_z = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); proof.opening_proof_at_z_omega = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); } function verify_serialized_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof) public view returns (bool) { VerificationKey memory vk = get_verification_key(); require(vk.num_inputs == public_inputs.length); Proof memory proof = deserialize_proof(public_inputs, serialized_proof); return verify(proof, vk); } }
File 4 of 4: Verifier
pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT library UncheckedMath { function uncheckedInc(uint256 _number) internal pure returns (uint256) { unchecked { return _number + 1; } } function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) { unchecked { return _lhs + _rhs; } } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT library PairingsBn254 { uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583; uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint256 constant bn254_b_coeff = 3; struct G1Point { uint256 X; uint256 Y; } struct Fr { uint256 value; } function new_fr(uint256 fr) internal pure returns (Fr memory) { require(fr < r_mod); return Fr({value: fr}); } function copy(Fr memory self) internal pure returns (Fr memory n) { n.value = self.value; } function assign(Fr memory self, Fr memory other) internal pure { self.value = other.value; } function inverse(Fr memory fr) internal view returns (Fr memory) { require(fr.value != 0); return pow(fr, r_mod - 2); } function add_assign(Fr memory self, Fr memory other) internal pure { self.value = addmod(self.value, other.value, r_mod); } function sub_assign(Fr memory self, Fr memory other) internal pure { self.value = addmod(self.value, r_mod - other.value, r_mod); } function mul_assign(Fr memory self, Fr memory other) internal pure { self.value = mulmod(self.value, other.value, r_mod); } function pow(Fr memory self, uint256 power) internal view returns (Fr memory) { uint256[6] memory input = [32, 32, 32, self.value, power, r_mod]; uint256[1] memory result; bool success; assembly { success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20) } require(success); return Fr({value: result[0]}); } // Encoding of field elements is: X[0] * z + X[1] struct G2Point { uint256[2] X; uint256[2] Y; } function P1() internal pure returns (G1Point memory) { return G1Point(1, 2); } function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) { return G1Point(x, y); } // function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) { function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) { if (x == 0 && y == 0) { // point of infinity is (0,0) return G1Point(x, y); } // check encoding require(x < q_mod, "x axis isn't valid"); require(y < q_mod, "y axis isn't valid"); // check on curve uint256 lhs = mulmod(y, y, q_mod); // y^2 uint256 rhs = mulmod(x, x, q_mod); // x^2 rhs = mulmod(rhs, x, q_mod); // x^3 rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b require(lhs == rhs, "is not on curve"); return G1Point(x, y); } function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) { return G2Point(x, y); } function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) { result.X = self.X; result.Y = self.Y; } function P2() internal pure returns (G2Point memory) { // for some reason ethereum expects to have c1*v + c0 form return G2Point( [ 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed ], [ 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa ] ); } function negate(G1Point memory self) internal pure { // The prime q in the base field F_q for G1 if (self.Y == 0) { require(self.X == 0); return; } self.Y = q_mod - self.Y; } function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { point_add_into_dest(p1, p2, r); return r; } function point_add_assign(G1Point memory p1, G1Point memory p2) internal view { point_add_into_dest(p1, p2, p1); } function point_add_into_dest( G1Point memory p1, G1Point memory p2, G1Point memory dest ) internal view { if (p2.X == 0 && p2.Y == 0) { // we add zero, nothing happens dest.X = p1.X; dest.Y = p1.Y; return; } else if (p1.X == 0 && p1.Y == 0) { // we add into zero, and we add non-zero point dest.X = p2.X; dest.Y = p2.Y; return; } else { uint256[4] memory input; input[0] = p1.X; input[1] = p1.Y; input[2] = p2.X; input[3] = p2.Y; bool success; assembly { success := staticcall(gas(), 6, input, 0x80, dest, 0x40) } require(success); } } function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view { point_sub_into_dest(p1, p2, p1); } function point_sub_into_dest( G1Point memory p1, G1Point memory p2, G1Point memory dest ) internal view { if (p2.X == 0 && p2.Y == 0) { // we subtracted zero, nothing happens dest.X = p1.X; dest.Y = p1.Y; return; } else if (p1.X == 0 && p1.Y == 0) { // we subtract from zero, and we subtract non-zero point dest.X = p2.X; dest.Y = q_mod - p2.Y; return; } else { uint256[4] memory input; input[0] = p1.X; input[1] = p1.Y; input[2] = p2.X; input[3] = q_mod - p2.Y; bool success = false; assembly { success := staticcall(gas(), 6, input, 0x80, dest, 0x40) } require(success); } } function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) { // https://eips.ethereum.org/EIPS/eip-197 // Elliptic curve points are encoded as a Jacobian pair (X, Y) where the point at infinity is encoded as (0, 0) if (p.X == 0 && p.Y == 1) { p.Y = 0; } point_mul_into_dest(p, s, r); return r; } function point_mul_assign(G1Point memory p, Fr memory s) internal view { point_mul_into_dest(p, s, p); } function point_mul_into_dest( G1Point memory p, Fr memory s, G1Point memory dest ) internal view { uint256[3] memory input; input[0] = p.X; input[1] = p.Y; input[2] = s.value; bool success; assembly { success := staticcall(gas(), 7, input, 0x60, dest, 0x40) } require(success); } function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) { require(p1.length == p2.length); uint256 elements = p1.length; uint256 inputSize = elements * 6; uint256[] memory input = new uint256[](inputSize); for (uint256 i = 0; i < elements; ) { input[i * 6 + 0] = p1[i].X; input[i * 6 + 1] = p1[i].Y; input[i * 6 + 2] = p2[i].X[0]; input[i * 6 + 3] = p2[i].X[1]; input[i * 6 + 4] = p2[i].Y[0]; input[i * 6 + 5] = p2[i].Y[1]; unchecked { ++i; } } uint256[1] memory out; bool success; assembly { success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) } require(success); return out[0] != 0; } /// Convenience method for a pairing check for two pairs. function pairingProd2( G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2 ) internal view returns (bool) { G1Point[] memory p1 = new G1Point[](2); G2Point[] memory p2 = new G2Point[](2); p1[0] = a1; p1[1] = b1; p2[0] = a2; p2[1] = b2; return pairing(p1, p2); } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./PairingsBn254.sol"; library TranscriptLib { // flip 0xe000000000000000000000000000000000000000000000000000000000000000; uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; uint32 constant DST_0 = 0; uint32 constant DST_1 = 1; uint32 constant DST_CHALLENGE = 2; struct Transcript { bytes32 state_0; bytes32 state_1; uint32 challenge_counter; } function new_transcript() internal pure returns (Transcript memory t) { t.state_0 = bytes32(0); t.state_1 = bytes32(0); t.challenge_counter = 0; } function update_with_u256(Transcript memory self, uint256 value) internal pure { bytes32 old_state_0 = self.state_0; self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value)); self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value)); } function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure { update_with_u256(self, value.value); } function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure { update_with_u256(self, p.X); update_with_u256(self, p.Y); } function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) { bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter)); self.challenge_counter += 1; challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK}); } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./libraries/PairingsBn254.sol"; import "./libraries/TranscriptLib.sol"; import "../common/libraries/UncheckedMath.sol"; uint256 constant STATE_WIDTH = 4; uint256 constant NUM_G2_ELS = 2; struct VerificationKey { uint256 domain_size; uint256 num_inputs; PairingsBn254.Fr omega; PairingsBn254.G1Point[2] gate_selectors_commitments; PairingsBn254.G1Point[8] gate_setup_commitments; PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments; PairingsBn254.G1Point lookup_selector_commitment; PairingsBn254.G1Point[4] lookup_tables_commitments; PairingsBn254.G1Point lookup_table_type_commitment; PairingsBn254.Fr[STATE_WIDTH - 1] non_residues; PairingsBn254.G2Point[NUM_G2_ELS] g2_elements; } contract Plonk4VerifierWithAccessToDNext { using PairingsBn254 for PairingsBn254.G1Point; using PairingsBn254 for PairingsBn254.G2Point; using PairingsBn254 for PairingsBn254.Fr; using TranscriptLib for TranscriptLib.Transcript; using UncheckedMath for uint256; struct Proof { uint256[] input_values; // commitments PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments; PairingsBn254.G1Point copy_permutation_grand_product_commitment; PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments; // openings PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z; PairingsBn254.Fr[1] state_polys_openings_at_z_omega; PairingsBn254.Fr[1] gate_selectors_openings_at_z; PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_polys_openings_at_z; PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega; PairingsBn254.Fr quotient_poly_opening_at_z; PairingsBn254.Fr linearization_poly_opening_at_z; // lookup commitments PairingsBn254.G1Point lookup_s_poly_commitment; PairingsBn254.G1Point lookup_grand_product_commitment; // lookup openings PairingsBn254.Fr lookup_s_poly_opening_at_z_omega; PairingsBn254.Fr lookup_grand_product_opening_at_z_omega; PairingsBn254.Fr lookup_t_poly_opening_at_z; PairingsBn254.Fr lookup_t_poly_opening_at_z_omega; PairingsBn254.Fr lookup_selector_poly_opening_at_z; PairingsBn254.Fr lookup_table_type_poly_opening_at_z; PairingsBn254.G1Point opening_proof_at_z; PairingsBn254.G1Point opening_proof_at_z_omega; } struct PartialVerifierState { PairingsBn254.Fr zero; PairingsBn254.Fr alpha; PairingsBn254.Fr beta; PairingsBn254.Fr gamma; PairingsBn254.Fr[9] alpha_values; PairingsBn254.Fr eta; PairingsBn254.Fr beta_lookup; PairingsBn254.Fr gamma_lookup; PairingsBn254.Fr beta_plus_one; PairingsBn254.Fr beta_gamma; PairingsBn254.Fr v; PairingsBn254.Fr u; PairingsBn254.Fr z; PairingsBn254.Fr z_omega; PairingsBn254.Fr z_minus_last_omega; PairingsBn254.Fr l_0_at_z; PairingsBn254.Fr l_n_minus_one_at_z; PairingsBn254.Fr t; PairingsBn254.G1Point tp; } function evaluate_l0_at_point(uint256 domain_size, PairingsBn254.Fr memory at) internal view returns (PairingsBn254.Fr memory num) { PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); PairingsBn254.Fr memory size_fe = PairingsBn254.new_fr(domain_size); PairingsBn254.Fr memory den = at.copy(); den.sub_assign(one); den.mul_assign(size_fe); den = den.inverse(); num = at.pow(domain_size); num.sub_assign(one); num.mul_assign(den); } function evaluate_lagrange_poly_out_of_domain( uint256 poly_num, uint256 domain_size, PairingsBn254.Fr memory omega, PairingsBn254.Fr memory at ) internal view returns (PairingsBn254.Fr memory res) { // (omega^i / N) / (X - omega^i) * (X^N - 1) require(poly_num < domain_size); PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); PairingsBn254.Fr memory omega_power = omega.pow(poly_num); res = at.pow(domain_size); res.sub_assign(one); require(res.value != 0); // Vanishing polynomial cannot be zero at point `at` res.mul_assign(omega_power); PairingsBn254.Fr memory den = PairingsBn254.copy(at); den.sub_assign(omega_power); den.mul_assign(PairingsBn254.new_fr(domain_size)); den = den.inverse(); res.mul_assign(den); } function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at) internal view returns (PairingsBn254.Fr memory res) { res = at.pow(domain_size); res.sub_assign(PairingsBn254.new_fr(1)); } function initialize_transcript(Proof memory proof, VerificationKey memory vk) internal pure returns (PartialVerifierState memory state) { TranscriptLib.Transcript memory transcript = TranscriptLib.new_transcript(); for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) { transcript.update_with_u256(proof.input_values[i]); } for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { transcript.update_with_g1(proof.state_polys_commitments[i]); } state.eta = transcript.get_challenge(); transcript.update_with_g1(proof.lookup_s_poly_commitment); state.beta = transcript.get_challenge(); state.gamma = transcript.get_challenge(); transcript.update_with_g1(proof.copy_permutation_grand_product_commitment); state.beta_lookup = transcript.get_challenge(); state.gamma_lookup = transcript.get_challenge(); transcript.update_with_g1(proof.lookup_grand_product_commitment); state.alpha = transcript.get_challenge(); for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { transcript.update_with_g1(proof.quotient_poly_parts_commitments[i]); } state.z = transcript.get_challenge(); transcript.update_with_fr(proof.quotient_poly_opening_at_z); for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) { transcript.update_with_fr(proof.state_polys_openings_at_z[i]); } for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) { transcript.update_with_fr(proof.state_polys_openings_at_z_omega[i]); } for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) { transcript.update_with_fr(proof.gate_selectors_openings_at_z[i]); } for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { transcript.update_with_fr(proof.copy_permutation_polys_openings_at_z[i]); } state.z_omega = state.z.copy(); state.z_omega.mul_assign(vk.omega); transcript.update_with_fr(proof.copy_permutation_grand_product_opening_at_z_omega); transcript.update_with_fr(proof.lookup_t_poly_opening_at_z); transcript.update_with_fr(proof.lookup_selector_poly_opening_at_z); transcript.update_with_fr(proof.lookup_table_type_poly_opening_at_z); transcript.update_with_fr(proof.lookup_s_poly_opening_at_z_omega); transcript.update_with_fr(proof.lookup_grand_product_opening_at_z_omega); transcript.update_with_fr(proof.lookup_t_poly_opening_at_z_omega); transcript.update_with_fr(proof.linearization_poly_opening_at_z); state.v = transcript.get_challenge(); transcript.update_with_g1(proof.opening_proof_at_z); transcript.update_with_g1(proof.opening_proof_at_z_omega); state.u = transcript.get_challenge(); } // compute some powers of challenge alpha([alpha^1, .. alpha^8]) function compute_powers_of_alpha(PartialVerifierState memory state) public pure { require(state.alpha.value != 0); state.alpha_values[0] = PairingsBn254.new_fr(1); state.alpha_values[1] = state.alpha.copy(); PairingsBn254.Fr memory current_alpha = state.alpha.copy(); for (uint256 i = 2; i < state.alpha_values.length; i = i.uncheckedInc()) { current_alpha.mul_assign(state.alpha); state.alpha_values[i] = current_alpha.copy(); } } function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) { // we initialize all challenges beforehand, we can draw each challenge in its own place PartialVerifierState memory state = initialize_transcript(proof, vk); if (verify_quotient_evaluation(vk, proof, state) == false) { return false; } require(proof.state_polys_openings_at_z_omega.length == 1); PairingsBn254.G1Point memory quotient_result = proof.quotient_poly_parts_commitments[0].copy_g1(); { // block scope PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size); PairingsBn254.Fr memory current_z = z_in_domain_size.copy(); PairingsBn254.G1Point memory tp; // start from i =1 for (uint256 i = 1; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { tp = proof.quotient_poly_parts_commitments[i].copy_g1(); tp.point_mul_assign(current_z); quotient_result.point_add_assign(tp); current_z.mul_assign(z_in_domain_size); } } Queries memory queries = prepare_queries(vk, proof, state); queries.commitments_at_z[0] = quotient_result; queries.values_at_z[0] = proof.quotient_poly_opening_at_z; queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state); queries.values_at_z[1] = proof.linearization_poly_opening_at_z; require(queries.commitments_at_z.length == queries.values_at_z.length); PairingsBn254.G1Point memory aggregated_commitment_at_z = queries.commitments_at_z[0]; PairingsBn254.Fr memory aggregated_opening_at_z = queries.values_at_z[0]; PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1); PairingsBn254.G1Point memory scaled; for (uint256 i = 1; i < queries.commitments_at_z.length; i = i.uncheckedInc()) { aggregation_challenge.mul_assign(state.v); scaled = queries.commitments_at_z[i].point_mul(aggregation_challenge); aggregated_commitment_at_z.point_add_assign(scaled); state.t = queries.values_at_z[i]; state.t.mul_assign(aggregation_challenge); aggregated_opening_at_z.add_assign(state.t); } aggregation_challenge.mul_assign(state.v); PairingsBn254.G1Point memory aggregated_commitment_at_z_omega = queries.commitments_at_z_omega[0].point_mul( aggregation_challenge ); PairingsBn254.Fr memory aggregated_opening_at_z_omega = queries.values_at_z_omega[0]; aggregated_opening_at_z_omega.mul_assign(aggregation_challenge); for (uint256 i = 1; i < queries.commitments_at_z_omega.length; i = i.uncheckedInc()) { aggregation_challenge.mul_assign(state.v); scaled = queries.commitments_at_z_omega[i].point_mul(aggregation_challenge); aggregated_commitment_at_z_omega.point_add_assign(scaled); state.t = queries.values_at_z_omega[i]; state.t.mul_assign(aggregation_challenge); aggregated_opening_at_z_omega.add_assign(state.t); } return final_pairing( vk.g2_elements, proof, state, aggregated_commitment_at_z, aggregated_commitment_at_z_omega, aggregated_opening_at_z, aggregated_opening_at_z_omega ); } function verify_quotient_evaluation( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) internal view returns (bool) { uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs); for (uint256 i = 0; i < lagrange_poly_numbers.length; i = i.uncheckedInc()) { lagrange_poly_numbers[i] = i; } require(vk.num_inputs > 0); PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0); for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) { state.t = evaluate_lagrange_poly_out_of_domain(i, vk.domain_size, vk.omega, state.z); state.t.mul_assign(PairingsBn254.new_fr(proof.input_values[i])); inputs_term.add_assign(state.t); } inputs_term.mul_assign(proof.gate_selectors_openings_at_z[0]); PairingsBn254.Fr memory result = proof.linearization_poly_opening_at_z.copy(); result.add_assign(inputs_term); // compute powers of alpha compute_powers_of_alpha(state); PairingsBn254.Fr memory factor = state.alpha_values[4].copy(); factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega); // - alpha_0 * (a + perm(z) * beta + gamma)*()*(d + gamma) * z(z*omega) require(proof.copy_permutation_polys_openings_at_z.length == STATE_WIDTH - 1); PairingsBn254.Fr memory t; // TMP; for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { t = proof.copy_permutation_polys_openings_at_z[i].copy(); t.mul_assign(state.beta); t.add_assign(proof.state_polys_openings_at_z[i]); t.add_assign(state.gamma); factor.mul_assign(t); } t = proof.state_polys_openings_at_z[3].copy(); t.add_assign(state.gamma); factor.mul_assign(t); result.sub_assign(factor); // - L_0(z) * alpha_1 PairingsBn254.Fr memory l_0_at_z = evaluate_l0_at_point(vk.domain_size, state.z); l_0_at_z.mul_assign(state.alpha_values[4 + 1]); result.sub_assign(l_0_at_z); PairingsBn254.Fr memory lookup_quotient_contrib = lookup_quotient_contribution(vk, proof, state); result.add_assign(lookup_quotient_contrib); PairingsBn254.Fr memory lhs = proof.quotient_poly_opening_at_z.copy(); lhs.mul_assign(evaluate_vanishing(vk.domain_size, state.z)); return lhs.value == result.value; } function lookup_quotient_contribution( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) internal view returns (PairingsBn254.Fr memory result) { PairingsBn254.Fr memory t; PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); state.beta_plus_one = state.beta_lookup.copy(); state.beta_plus_one.add_assign(one); state.beta_gamma = state.beta_plus_one.copy(); state.beta_gamma.mul_assign(state.gamma_lookup); // (s'*beta + gamma)*(zw')*alpha t = proof.lookup_s_poly_opening_at_z_omega.copy(); t.mul_assign(state.beta_lookup); t.add_assign(state.beta_gamma); t.mul_assign(proof.lookup_grand_product_opening_at_z_omega); t.mul_assign(state.alpha_values[6]); // (z - omega^{n-1}) for this part PairingsBn254.Fr memory last_omega = vk.omega.pow(vk.domain_size - 1); state.z_minus_last_omega = state.z.copy(); state.z_minus_last_omega.sub_assign(last_omega); t.mul_assign(state.z_minus_last_omega); result.add_assign(t); // - alpha_1 * L_{0}(z) state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z); t = state.l_0_at_z.copy(); t.mul_assign(state.alpha_values[6 + 1]); result.sub_assign(t); // - alpha_2 * beta_gamma_powered L_{n-1}(z) PairingsBn254.Fr memory beta_gamma_powered = state.beta_gamma.pow(vk.domain_size - 1); state.l_n_minus_one_at_z = evaluate_lagrange_poly_out_of_domain( vk.domain_size - 1, vk.domain_size, vk.omega, state.z ); t = state.l_n_minus_one_at_z.copy(); t.mul_assign(beta_gamma_powered); t.mul_assign(state.alpha_values[6 + 2]); result.sub_assign(t); } function aggregated_linearization_commitment( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) internal view returns (PairingsBn254.G1Point memory result) { // qMain*(Q_a * A + Q_b * B + Q_c * C + Q_d * D + Q_m * A*B + Q_const + Q_dNext * D_next) result = PairingsBn254.new_g1(0, 0); // Q_a * A PairingsBn254.G1Point memory scaled = vk.gate_setup_commitments[0].point_mul( proof.state_polys_openings_at_z[0] ); result.point_add_assign(scaled); // Q_b * B scaled = vk.gate_setup_commitments[1].point_mul(proof.state_polys_openings_at_z[1]); result.point_add_assign(scaled); // Q_c * C scaled = vk.gate_setup_commitments[2].point_mul(proof.state_polys_openings_at_z[2]); result.point_add_assign(scaled); // Q_d * D scaled = vk.gate_setup_commitments[3].point_mul(proof.state_polys_openings_at_z[3]); result.point_add_assign(scaled); // Q_m* A*B or Q_ab*A*B PairingsBn254.Fr memory t = proof.state_polys_openings_at_z[0].copy(); t.mul_assign(proof.state_polys_openings_at_z[1]); scaled = vk.gate_setup_commitments[4].point_mul(t); result.point_add_assign(scaled); // Q_AC* A*C t = proof.state_polys_openings_at_z[0].copy(); t.mul_assign(proof.state_polys_openings_at_z[2]); scaled = vk.gate_setup_commitments[5].point_mul(t); result.point_add_assign(scaled); // Q_const result.point_add_assign(vk.gate_setup_commitments[6]); // Q_dNext * D_next scaled = vk.gate_setup_commitments[7].point_mul(proof.state_polys_openings_at_z_omega[0]); result.point_add_assign(scaled); result.point_mul_assign(proof.gate_selectors_openings_at_z[0]); PairingsBn254.G1Point memory rescue_custom_gate_linearization_contrib = rescue_custom_gate_linearization_contribution( vk, proof, state ); result.point_add_assign(rescue_custom_gate_linearization_contrib); require(vk.non_residues.length == STATE_WIDTH - 1); PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); PairingsBn254.Fr memory factor = state.alpha_values[4].copy(); for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; ) { t = state.z.copy(); if (i == 0) { t.mul_assign(one); } else { t.mul_assign(vk.non_residues[i - 1]); } t.mul_assign(state.beta); t.add_assign(state.gamma); t.add_assign(proof.state_polys_openings_at_z[i]); factor.mul_assign(t); unchecked { ++i; } } scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor); result.point_add_assign(scaled); // - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X) factor = state.alpha_values[4].copy(); factor.mul_assign(state.beta); factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega); for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { t = proof.copy_permutation_polys_openings_at_z[i].copy(); t.mul_assign(state.beta); t.add_assign(state.gamma); t.add_assign(proof.state_polys_openings_at_z[i]); factor.mul_assign(t); } scaled = vk.permutation_commitments[3].point_mul(factor); result.point_sub_assign(scaled); // + L_0(z) * Z(x) state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z); require(state.l_0_at_z.value != 0); factor = state.l_0_at_z.copy(); factor.mul_assign(state.alpha_values[4 + 1]); scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor); result.point_add_assign(scaled); PairingsBn254.G1Point memory lookup_linearization_contrib = lookup_linearization_contribution(proof, state); result.point_add_assign(lookup_linearization_contrib); } function rescue_custom_gate_linearization_contribution( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) public view returns (PairingsBn254.G1Point memory result) { PairingsBn254.Fr memory t; PairingsBn254.Fr memory intermediate_result; // a^2 - b = 0 t = proof.state_polys_openings_at_z[0].copy(); t.mul_assign(t); t.sub_assign(proof.state_polys_openings_at_z[1]); // t.mul_assign(challenge1); t.mul_assign(state.alpha_values[1]); intermediate_result.add_assign(t); // b^2 - c = 0 t = proof.state_polys_openings_at_z[1].copy(); t.mul_assign(t); t.sub_assign(proof.state_polys_openings_at_z[2]); t.mul_assign(state.alpha_values[1 + 1]); intermediate_result.add_assign(t); // c*a - d = 0; t = proof.state_polys_openings_at_z[2].copy(); t.mul_assign(proof.state_polys_openings_at_z[0]); t.sub_assign(proof.state_polys_openings_at_z[3]); t.mul_assign(state.alpha_values[1 + 2]); intermediate_result.add_assign(t); result = vk.gate_selectors_commitments[1].point_mul(intermediate_result); } function lookup_linearization_contribution(Proof memory proof, PartialVerifierState memory state) internal view returns (PairingsBn254.G1Point memory result) { PairingsBn254.Fr memory zero = PairingsBn254.new_fr(0); PairingsBn254.Fr memory t; PairingsBn254.Fr memory factor; // s(x) from the Z(x*omega)*(\\gamma*(1 + \\beta) + s(x) + \\beta * s(x*omega))) factor = proof.lookup_grand_product_opening_at_z_omega.copy(); factor.mul_assign(state.alpha_values[6]); factor.mul_assign(state.z_minus_last_omega); PairingsBn254.G1Point memory scaled = proof.lookup_s_poly_commitment.point_mul(factor); result.point_add_assign(scaled); // Z(x) from - alpha_0 * Z(x) * (\\beta + 1) * (\\gamma + f(x)) * (\\gamma(1 + \\beta) + t(x) + \\beta * t(x*omega)) // + alpha_1 * Z(x) * L_{0}(z) + alpha_2 * Z(x) * L_{n-1}(z) // accumulate coefficient factor = proof.lookup_t_poly_opening_at_z_omega.copy(); factor.mul_assign(state.beta_lookup); factor.add_assign(proof.lookup_t_poly_opening_at_z); factor.add_assign(state.beta_gamma); // (\\gamma + f(x)) PairingsBn254.Fr memory f_reconstructed; PairingsBn254.Fr memory current = PairingsBn254.new_fr(1); PairingsBn254.Fr memory tmp0; for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { tmp0 = proof.state_polys_openings_at_z[i].copy(); tmp0.mul_assign(current); f_reconstructed.add_assign(tmp0); current.mul_assign(state.eta); } // add type of table t = proof.lookup_table_type_poly_opening_at_z.copy(); t.mul_assign(current); f_reconstructed.add_assign(t); f_reconstructed.mul_assign(proof.lookup_selector_poly_opening_at_z); f_reconstructed.add_assign(state.gamma_lookup); // end of (\\gamma + f(x)) part factor.mul_assign(f_reconstructed); factor.mul_assign(state.beta_plus_one); t = zero.copy(); t.sub_assign(factor); factor = t; factor.mul_assign(state.alpha_values[6]); // Multiply by (z - omega^{n-1}) factor.mul_assign(state.z_minus_last_omega); // L_{0}(z) in front of Z(x) t = state.l_0_at_z.copy(); t.mul_assign(state.alpha_values[6 + 1]); factor.add_assign(t); // L_{n-1}(z) in front of Z(x) t = state.l_n_minus_one_at_z.copy(); t.mul_assign(state.alpha_values[6 + 2]); factor.add_assign(t); scaled = proof.lookup_grand_product_commitment.point_mul(factor); result.point_add_assign(scaled); } struct Queries { PairingsBn254.G1Point[13] commitments_at_z; PairingsBn254.Fr[13] values_at_z; PairingsBn254.G1Point[6] commitments_at_z_omega; PairingsBn254.Fr[6] values_at_z_omega; } function prepare_queries( VerificationKey memory vk, Proof memory proof, PartialVerifierState memory state ) public view returns (Queries memory queries) { // we set first two items in calee side so start idx from 2 uint256 idx = 2; for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { queries.commitments_at_z[idx] = proof.state_polys_commitments[i]; queries.values_at_z[idx] = proof.state_polys_openings_at_z[i]; idx = idx.uncheckedInc(); } require(proof.gate_selectors_openings_at_z.length == 1); queries.commitments_at_z[idx] = vk.gate_selectors_commitments[0]; queries.values_at_z[idx] = proof.gate_selectors_openings_at_z[0]; idx = idx.uncheckedInc(); for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { queries.commitments_at_z[idx] = vk.permutation_commitments[i]; queries.values_at_z[idx] = proof.copy_permutation_polys_openings_at_z[i]; idx = idx.uncheckedInc(); } queries.commitments_at_z_omega[0] = proof.copy_permutation_grand_product_commitment; queries.commitments_at_z_omega[1] = proof.state_polys_commitments[STATE_WIDTH - 1]; queries.values_at_z_omega[0] = proof.copy_permutation_grand_product_opening_at_z_omega; queries.values_at_z_omega[1] = proof.state_polys_openings_at_z_omega[0]; PairingsBn254.G1Point memory lookup_t_poly_commitment_aggregated = vk.lookup_tables_commitments[0]; PairingsBn254.Fr memory current_eta = state.eta.copy(); for (uint256 i = 1; i < vk.lookup_tables_commitments.length; i = i.uncheckedInc()) { state.tp = vk.lookup_tables_commitments[i].point_mul(current_eta); lookup_t_poly_commitment_aggregated.point_add_assign(state.tp); current_eta.mul_assign(state.eta); } queries.commitments_at_z[idx] = lookup_t_poly_commitment_aggregated; queries.values_at_z[idx] = proof.lookup_t_poly_opening_at_z; idx = idx.uncheckedInc(); queries.commitments_at_z[idx] = vk.lookup_selector_commitment; queries.values_at_z[idx] = proof.lookup_selector_poly_opening_at_z; idx = idx.uncheckedInc(); queries.commitments_at_z[idx] = vk.lookup_table_type_commitment; queries.values_at_z[idx] = proof.lookup_table_type_poly_opening_at_z; queries.commitments_at_z_omega[2] = proof.lookup_s_poly_commitment; queries.values_at_z_omega[2] = proof.lookup_s_poly_opening_at_z_omega; queries.commitments_at_z_omega[3] = proof.lookup_grand_product_commitment; queries.values_at_z_omega[3] = proof.lookup_grand_product_opening_at_z_omega; queries.commitments_at_z_omega[4] = lookup_t_poly_commitment_aggregated; queries.values_at_z_omega[4] = proof.lookup_t_poly_opening_at_z_omega; } function final_pairing( // VerificationKey memory vk, PairingsBn254.G2Point[NUM_G2_ELS] memory g2_elements, Proof memory proof, PartialVerifierState memory state, PairingsBn254.G1Point memory aggregated_commitment_at_z, PairingsBn254.G1Point memory aggregated_commitment_at_z_omega, PairingsBn254.Fr memory aggregated_opening_at_z, PairingsBn254.Fr memory aggregated_opening_at_z_omega ) internal view returns (bool) { // q(x) = f(x) - f(z) / (x - z) // q(x) * (x-z) = f(x) - f(z) // f(x) PairingsBn254.G1Point memory pair_with_generator = aggregated_commitment_at_z.copy_g1(); aggregated_commitment_at_z_omega.point_mul_assign(state.u); pair_with_generator.point_add_assign(aggregated_commitment_at_z_omega); // - f(z)*g PairingsBn254.Fr memory aggregated_value = aggregated_opening_at_z_omega.copy(); aggregated_value.mul_assign(state.u); aggregated_value.add_assign(aggregated_opening_at_z); PairingsBn254.G1Point memory tp = PairingsBn254.P1().point_mul(aggregated_value); pair_with_generator.point_sub_assign(tp); // +z * q(x) tp = proof.opening_proof_at_z.point_mul(state.z); PairingsBn254.Fr memory t = state.z_omega.copy(); t.mul_assign(state.u); PairingsBn254.G1Point memory t1 = proof.opening_proof_at_z_omega.point_mul(t); tp.point_add_assign(t1); pair_with_generator.point_add_assign(tp); // rhs PairingsBn254.G1Point memory pair_with_x = proof.opening_proof_at_z_omega.point_mul(state.u); pair_with_x.point_add_assign(proof.opening_proof_at_z); pair_with_x.negate(); // Pairing precompile expects points to be in a `i*x[1] + x[0]` form instead of `x[0] + i*x[1]` // so we handle it in code generation step PairingsBn254.G2Point memory first_g2 = g2_elements[0]; PairingsBn254.G2Point memory second_g2 = g2_elements[1]; return PairingsBn254.pairingProd2(pair_with_generator, first_g2, pair_with_x, second_g2); } } pragma solidity ^0.8.13; // SPDX-License-Identifier: MIT import "./Plonk4VerifierWithAccessToDNext.sol"; import "../common/libraries/UncheckedMath.sol"; contract Verifier is Plonk4VerifierWithAccessToDNext { using UncheckedMath for uint256; function get_verification_key() public pure returns (VerificationKey memory vk) { vk.num_inputs = 1; vk.domain_size = 67108864; vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97); // coefficients vk.gate_setup_commitments[0] = PairingsBn254.new_g1( 0x14c289d746e37aa82ec428491881c4732766492a8bc2e8e3cca2000a40c0ea27, 0x2f617a7eb9808ad9843d1e080b7cfbf99d61bb1b02076c905f31adb12731bc41 ); vk.gate_setup_commitments[1] = PairingsBn254.new_g1( 0x210b5cc8e6a85d63b65b701b8fb5ad24ff9c41f923432de17fe4ebae04526a8c, 0x05c10ab17ea731b2b87fb890fa5b10bd3d6832917a616b807a9b640888ebc731 ); vk.gate_setup_commitments[2] = PairingsBn254.new_g1( 0x29d4d14adcfe67a2ac690d6369db6b75e82d8ab3124bc4fa1dd145f41ca6949c, 0x004f6cd229373f1c1f735ccf49aef6a5c32025bc36c3328596dd0db7d87bef67 ); vk.gate_setup_commitments[3] = PairingsBn254.new_g1( 0x06d15382e8cabae9f98374a9fbdadd424f48e24da7e4c65bf710fd7d7d59a05a, 0x22e438ad5c51673879ce17073a3d2d29327a97dc3ce61c4f88540e00087695f6 ); vk.gate_setup_commitments[4] = PairingsBn254.new_g1( 0x274a668dfc485cf192d0086f214146d9e02b3040a5a586df344c53c16a87882b, 0x15f5bb7ad01f162b70fc77c8ea456d67d15a6ce98acbbfd521222810f8ec0a66 ); vk.gate_setup_commitments[5] = PairingsBn254.new_g1( 0x0ba53bf4fb0446927857e33978d02abf45948fc68f4091394ae0827a22cf1e47, 0x0720d818751ce5b3f11c716e925f60df4679ea90bed516499bdec066f5ff108f ); vk.gate_setup_commitments[6] = PairingsBn254.new_g1( 0x2e986ba2ea495e5ec6af532980b1dc567f1430bfa82f8de07c12fc097c0e0483, 0x1555d189f6164e82d78de1b8313c2e923e616b3c8ed0e350c3b61c94516d0b58 ); vk.gate_setup_commitments[7] = PairingsBn254.new_g1( 0x0925959592604ca73c917f9b2e029aa2563c318ddcc5ca29c11badb7b880127b, 0x2b4a430fcb2fa7d6d67d6c358e01cf0524c7df7e1e56442f65b39bc1a1052367 ); // gate selectors vk.gate_selectors_commitments[0] = PairingsBn254.new_g1( 0x28f2a0a95af79ba67e9dd1986bd3190199f661b710a693fc82fb395c126edcbd, 0x0db75db5de5192d1ba1c24710fc00da16fa8029ac7fe82d855674dcd6d090e05 ); vk.gate_selectors_commitments[1] = PairingsBn254.new_g1( 0x143471a174dfcb2d9cb5ae621e519387bcc93c9dcfc011160b2f5c5f88e32cbe, 0x2a0194c0224c3d964223a96c4c99e015719bc879125aa0df3f0715d154e71a31 ); // permutation vk.permutation_commitments[0] = PairingsBn254.new_g1( 0x1423fa82e00ba22c280181afb12c56eea541933eeb5ec39119b0365b6beab4b9, 0x0efdcd3423a38f5e2ecf8c7e4fd46f13189f8fed392ad9d8d393e8ba568b06e4 ); vk.permutation_commitments[1] = PairingsBn254.new_g1( 0x0e9b5b12c1090d62224e64aa1696c009aa59a9c3eec458e781fae773e1f4eca5, 0x1fe3df508c7e9750eb37d9cae5e7437ad11a21fa36530ff821b407b165a79a55 ); vk.permutation_commitments[2] = PairingsBn254.new_g1( 0x25d1a714bd1e258f196e38d6b2826153382c2d04b870d0b7ec250296005129ae, 0x0883a121b41ca7beaa9de97ecf4417e62aa2eeb9434f24ddacbfed57cbf016a8 ); vk.permutation_commitments[3] = PairingsBn254.new_g1( 0x2f3ede68e854a6b3b14589851cf077a606e2aeb3205c43cc579b7abae39d8f58, 0x178ccd4b1f78fd79ee248e376b6fc8297d5450900d1e15e8c03e3ed2c171ac8c ); // lookup table commitments vk.lookup_selector_commitment = PairingsBn254.new_g1( 0x1f814e2d87c332e964eeef94ec695eef9d2caaac58b682a43da5107693b06f30, 0x196d56fb01907e66af9303886fd95328d398e5b2b72906882a9d12c1718e2ee2 ); vk.lookup_tables_commitments[0] = PairingsBn254.new_g1( 0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c, 0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b ); vk.lookup_tables_commitments[1] = PairingsBn254.new_g1( 0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9, 0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca ); vk.lookup_tables_commitments[2] = PairingsBn254.new_g1( 0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44, 0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765 ); vk.lookup_tables_commitments[3] = PairingsBn254.new_g1( 0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e, 0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e ); vk.lookup_table_type_commitment = PairingsBn254.new_g1( 0x2f85df2d6249ccbcc11b91727333cc800459de6ee274f29c657c8d56f6f01563, 0x088e1df178c47116a69c3c8f6d0c5feb530e2a72493694a623b1cceb7d44a76c ); // non residues vk.non_residues[0] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000005); vk.non_residues[1] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000007); vk.non_residues[2] = PairingsBn254.new_fr(0x000000000000000000000000000000000000000000000000000000000000000a); // g2 elements vk.g2_elements[0] = PairingsBn254.new_g2( [ 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed ], [ 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa ] ); vk.g2_elements[1] = PairingsBn254.new_g2( [ 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1, 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 ], [ 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4, 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 ] ); } function deserialize_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof) internal pure returns (Proof memory proof) { require(serialized_proof.length == 44); proof.input_values = new uint256[](public_inputs.length); for (uint256 i = 0; i < public_inputs.length; i = i.uncheckedInc()) { proof.input_values[i] = public_inputs[i]; } uint256 j; for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { proof.state_polys_commitments[i] = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); } proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); proof.lookup_s_poly_commitment = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); proof.lookup_grand_product_commitment = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { proof.quotient_poly_parts_commitments[i] = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); } for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) { proof.state_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); } for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) { proof.state_polys_openings_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); } for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) { proof.gate_selectors_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); } for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { proof.copy_permutation_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); } proof.copy_permutation_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_s_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_t_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_t_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_selector_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.lookup_table_type_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.quotient_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.linearization_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); j = j.uncheckedInc(); proof.opening_proof_at_z = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); j = j.uncheckedAdd(2); proof.opening_proof_at_z_omega = PairingsBn254.new_g1_checked( serialized_proof[j], serialized_proof[j.uncheckedInc()] ); } function verify_serialized_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof) public view returns (bool) { VerificationKey memory vk = get_verification_key(); require(vk.num_inputs == public_inputs.length); Proof memory proof = deserialize_proof(public_inputs, serialized_proof); return verify(proof, vk); } }