Address Contract Verified
Address
0x1817874feAb3ce053d0F40AbC23870DB35C2AFfc
Balance
0 ETH
Nonce
1
Code Size
11812 bytes
Creator
0x4B8a782D...e967 at tx 0x755ea003...0f57c9
Indexed Transactions
0
Contract Bytecode
11812 bytes
0x60806040526004361015610011575f80fd5b5f3560e01c80630a7c8faa146101045780632cdea717146100ff57806336667513146100fa57806341c9634e146100f5578063591d99ee146100f05780635da57fe9146100eb578063623b223d146100e657806366ae69a0146100e15780636f55bd32146100dc5780638ab81d13146100d7578063a401662b146100d2578063a77cf3d2146100cd578063ad209a9b146100c8578063bb51f1eb146100c35763df0dd0d5146100be575f80fd5b610a58565b6109d8565b61099e565b6107f7565b6107a7565b61068b565b610651565b61062a565b610574565b61048c565b6103e8565b6103cc565b610314565b610258565b3461013e575f60031936011261013e5760206040517f6d680000000000000000000000000000000000000000000000000000000000008152f35b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761018b57604052565b610142565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761018b57604052565b604051906101e060a083610190565b565b92916fffffffffffffffffffffffffffffffff809216845216602083015260408201526080606082015260c0810191805192604060808401528351809152602060e084019401905f5b81811061024257505050602060a091015191015290565b825186526020958601959092019160010161022b565b3461013e575f60031936011261013e576002546003549060405161027b8161016f565b604051600480548083525f91825260208301917f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b91905b8181106102fe57505050906102cd816102fa94930382610190565b8152600554602082015260405193836fffffffffffffffffffffffffffffffff869560801c9116856101e2565b0390f35b82548452602090930192600192830192016102b2565b3461013e575f60031936011261013e57600654600754906040516103378161016f565b604051600880548083525f91825260208301917ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee391905b8181106103b65750505090610389816102fa94930382610190565b8152600954602082015260405193836fffffffffffffffffffffffffffffffff869560801c9116856101e2565b825484526020909301926001928301920161036e565b3461013e575f60031936011261013e5760205f54604051908152f35b3461013e575f60031936011261013e5760206040517f00000000000000000000000000000000000000000000000000000000000000808152f35b9181601f8401121561013e5782359167ffffffffffffffff831161013e576020808501948460051b01011161013e57565b60206040818301928281528451809452019201905f5b8181106104765750505090565b8251845260209384019390920191600101610469565b3461013e57604060031936011261013e5760043567ffffffffffffffff811161013e576104bd903690600401610422565b9060243582811061050f576104d46104d991612253565b61157c565b915f5b8181106104f157604051806102fa8682610453565b8061050961050260019385876115f8565b35866126a9565b016104dc565b7f5c85a0e7000000000000000000000000000000000000000000000000000000005f5260045ffd5b9081606091031261013e5790565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c60e091011261013e57606490565b3461013e5761018060031936011261013e5760043567ffffffffffffffff811161013e576105a6903690600401610537565b60243567ffffffffffffffff811161013e576105c6903690600401610422565b60449291923567ffffffffffffffff811161013e576105e9903690600401610422565b6105f594919436610545565b90610144359567ffffffffffffffff871161013e5761061b610628973690600401610422565b9590946101643597610c99565b005b3461013e575f60031936011261013e57602067ffffffffffffffff60015416604051908152f35b3461013e575f60031936011261013e5760206040517f00000000000000000000000000000000000000000000000000000000000000118152f35b3461013e57604060031936011261013e5760043560243567ffffffffffffffff811161013e576106bf903690600401610422565b9091335f5260205260405f205f52600a60205260405f20916002830154604051602081019061071f816106f3848888611032565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610190565b5190200361077f57826107739263ffffffff8061076960016102fa9801549454956107616107548863ffffffff9060401c1690565b9760601c63ffffffff1690565b94369161107e565b9216931691611edb565b60405191829182610453565b7f6768c0aa000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461013e57606060031936011261013e5760043560243567ffffffffffffffff811161013e576020916107e16107ed923690600401610422565b90604435925f54611df8565b6040519015158152f35b3461013e57602060031936011261013e5761081b600435335f5260205260405f2090565b61082d815f52600a60205260405f2090565b90610840825467ffffffffffffffff1690565b9167ffffffffffffffff83169081156109765760010192835461094e576108887f000000000000000000000000000000000000000000000000000000000000008080936110ca565b4310610926576108cc916108a59167ffffffffffffffff166110ca565b7f0000000000000000000000000000000000000000000000000000000000000018906110ca565b43116108d85750449055005b6108ed6108fe915f52600a60205260405f2090565b60025f918281558260018201550155565b7f40d35447000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc77c1949000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe31d9005000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f6686db64000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461013e575f60031936011261013e5760206040517f00000000000000000000000000000000000000000000000000000000000000188152f35b3461013e57606060031936011261013e5760043567ffffffffffffffff811161013e57610a09903690600401610537565b60243567ffffffffffffffff811161013e57610a29903690600401610422565b604435929167ffffffffffffffff841161013e5760c0600319853603011261013e57610628936004019261115a565b3461013e57602060031936011261013e576004355f52600a60205260a060405f20805490600260018201549101549063ffffffff6040519367ffffffffffffffff81168552818160401c16602086015260601c16604084015260608301526080820152f35b3567ffffffffffffffff8116810361013e5790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b6fffffffffffffffffffffffffffffffff6001911601906fffffffffffffffffffffffffffffffff8211610b2f57565b610ad2565b9068010000000000000000811161018b57815491818155828210610b5757505050565b5f5260205f2091820191015b818110610b6e575050565b5f8155600101610b63565b818114610bd35781549167ffffffffffffffff831161018b57610b9c8383610b34565b5f5260205f20905f5260205f208154915f925b848410610bbd575050505050565b6001809192019384549281850155019290610baf565b5050565b6006546fffffffffffffffffffffffffffffffff81167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617600255600754600355610c2760086004610b79565b600954600555565b3563ffffffff8116810361013e5790565b9081519182519267ffffffffffffffff841161018b57602090610c64856008610b34565b019260085f5260205f205f5b828110610c8557505050602001516009559050565b600190602087519701968184015501610c70565b9790969293610d1f9594899793610caf896116b4565b6020815191012090610cd4818c610ccd85335f5260205260405f2090565b9c8d6118c4565b5f600260208e01610ce481610abd565b67ffffffffffffffff610d086006546fffffffffffffffffffffffffffffffff1690565b9d8e6fffffffffffffffffffffffffffffffff1690565b911603610fc05750505090610d3c93929160019b8b6006936119c1565b610d4588611bf4565b96610dfd575b50505050507fd95fe1258d152dc91c81b09380498adc76ed36a6079bcb2ed31eff622ae2d0f192610dda6108ed610ddf93855f55610dcc610d97610d8e86610c2f565b63ffffffff1690565b67ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006001541617600155565b5f52600a60205260405f2090565b610c2f565b6040805192835263ffffffff9091166020830152819081015b0390a1565b60608401979467ffffffffffffffff610e36610e21610e1b8c610abd565b93610aff565b6fffffffffffffffffffffffffffffffff1690565b911603610f9857610e5f92610e5b92610e4e86611d0e565b6020815191012089611df8565b1590565b610f70576108ed610ddf93610f68610f63610f5e610d8e610dda96610efc610ebf610eb27fd95fe1258d152dc91c81b09380498adc76ed36a6079bcb2ed31eff622ae2d0f19e610ead610bd7565b610abd565b67ffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006006541617600655565b610dda60a06080830192610f57610f15610d8e86610c2f565b6fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff000000000000000000000000000000006006549260801b16911617600655565b0135600755565b611e7d565b610c40565b938396610d4b565b7f128597bb000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc72c8200000000000000000000000000000000000000000000000000000000005f5260045ffd5b610fd2909d9193929d96959496610abd565b67ffffffffffffffff610ff9610e216002546fffffffffffffffffffffffffffffffff1690565b91160361100a578b610d3c966119c1565b7fc06789fa000000000000000000000000000000000000000000000000000000005f5260045ffd5b91907f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811161013e5760051b809282370190565b67ffffffffffffffff811161018b5760051b60200190565b92919061108a81611066565b936110986040519586610190565b602085838152019160051b810192831161013e57905b8282106110ba57505050565b81358152602091820191016110ae565b91908201809211610b2f57565b3573ffffffffffffffffffffffffffffffffffffffff8116810361013e5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561013e570180359067ffffffffffffffff821161013e57602001918160051b3603831361013e57565b3560ff8116810361013e5790565b92909261116681610c2f565b63ffffffff611181610eb260015467ffffffffffffffff1690565b91161115611554576002936020820161119981610abd565b67ffffffffffffffff6111c0610e216002546fffffffffffffffffffffffffffffffff1690565b9116036114f257506060830135946111ea6111da87612032565b966111e488612067565b90612132565b608084016111f7816110d7565b61121a610e5b6060880135809361121160a08b018b6110f8565b929091886121b2565b9081156114d3575b506114ab5773ffffffffffffffffffffffffffffffffffffffff61128f61127661127061124e886116b4565b602081519101209861125f8161114c565b60408201359160200135908b61223c565b936110d7565b73ffffffffffffffffffffffffffffffffffffffff1690565b911603611483575460801c806112a481612253565b86149081159161144a575b5061077f576113a5610dcc61142d95610dda9461138d610df8996106f3611356611343610d8e7fbee983fc706c692efb9b0240bddc5666c010a53af55ed5fb42d226e7e42938699f6113048c63ffffffff1690565b9b6fffffffffffffffffffffffffffffffff61ffff7f00000000000000000000000000000000000000000000000000000000000000119316911661262e565b9460405192839160208301958691611032565b519020916113806113656101d1565b4367ffffffffffffffff1681529763ffffffff166020890152565b63ffffffff166040870152565b5f60608601526080850152335f5260205260405f2090565b8151815460208401516040808601517fffffffffffffffffffffffffffffffff0000000000000000000000000000000090931667ffffffffffffffff9490941693909317921b6bffffffff00000000000000001691909117606091821b6fffffffff000000000000000000000000161782558201516001820155608090910151600290910155565b6040805133815263ffffffff909216602083015290918291820190565b905061147c61147661145d36898761107e565b6fffffffffffffffffffffffffffffffff851690612264565b916125e9565b115f6112af565b7f8baa579f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe00153fa000000000000000000000000000000000000000000000000000000005f5260045ffd5b6114ec9150610e5b906114e7368a8861107e565b61221f565b5f611222565b6114fd919550610abd565b67ffffffffffffffff611524610e216006546fffffffffffffffffffffffffffffffff1690565b91160361100a5760608201359361154d61153d86611fcf565b9561154787612067565b90612082565b60066111ea565b7f3d618e50000000000000000000000000000000000000000000000000000000005f5260045ffd5b9061158682611066565b6115936040519182610190565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06115c18294611066565b0190602036910137565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b91908110156116085760051b0190565b6115cb565b805191908290602001825e015f815290565b600c906101e0927fffffffffffffffff00000000000000000000000000000000000000000000000061167d96957fffffffff00000000000000000000000000000000000000000000000000000000604051988995602087019061160d565b93168352166004820152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec810185520183610190565b6116c160408201826110f8565b916116cb83612b66565b915f935b8085106117f15750506117ee92506117e861174860206117416116f185610c2f565b7fffffffff0000000000000000000000000000000000000000000000000000000090600881811b63ff00ff001691901c62ff00ff1617601081811b63ffff00001691901c61ffff161760e01b1690565b9301610abd565b7fffffffffffffffff0000000000000000000000000000000000000000000000009067ffffffffffff000067ff00ff00ff00ff0066ff00ff00ff00ff8360081c169260081b169165ffff0000ffff65ffff0000ff0065ffffffffffff67ffff0000ffff0000861666ff0000ffff000085161760101c16941691161760101b161767ffffffff0000000063ffffffff8260201c169160201b161760c01b1690565b9161161f565b90565b90919261187b6118b9600192600261181261180d8a888a611afb565b611b3b565b61183461182d6118238c8a8c611afb565b6020810190611b68565b9050612b66565b6118866118456118238d8b8d611afb565b94859391947fffff0000000000000000000000000000000000000000000000000000000000006040519a8b9960208b019061160d565b91168152019061160d565b918237015f8152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610190565b9401939291906116cf565b6118d9909392935f52600a60205260405f2090565b9067ffffffffffffffff6118f5835467ffffffffffffffff1690565b1615610976576001820154156119595761190e90610c2f565b63ffffffff611929610eb260015467ffffffffffffffff1690565b911611156115545761194f60026106f39201549360405192839160208301958691611032565b5190200361077f57565b7f78ef3a47000000000000000000000000000000000000000000000000000000005f5260045ffd5b91908110156116085760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff418136030182121561013e570190565b92906119d8909694965f52600a60205260405f2090565b916119ee610d8e845463ffffffff9060601c1690565b91828603611ad3576fffffffffffffffffffffffffffffffff611a266001611a2e96015493611a1e8b5460801c90565b93369161107e565b911691611edb565b915f5b818110611a4057505050505050565b611a4b818387611981565b9060808201611a59816110d7565b92611a7d610e5b6060830135958b87611a7560a08701876110f8565b9390926121b2565b6114ab57611a8e610e5b858961221f565b6114ab57611aba6112766112708361125f73ffffffffffffffffffffffffffffffffffffffff9561114c565b91160361148357611acd600192866126cf565b01611a31565b7f1f1711da000000000000000000000000000000000000000000000000000000005f5260045ffd5b91908110156116085760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18136030182121561013e570190565b357fffff0000000000000000000000000000000000000000000000000000000000008116810361013e5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561013e570180359067ffffffffffffffff821161013e5760200191813603831361013e57565b359060208110611bc7575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b5f905b60408101611c0581836110f8565b9050831015611ce6577f6d68000000000000000000000000000000000000000000000000000000000000611c6e611c4961180d86611c4386886110f8565b90611afb565b7fffff0000000000000000000000000000000000000000000000000000000000001690565b14611c7e57509060010190611bf7565b6020611c9a611c9185611c4385876110f8565b82810190611b68565b905014155f14611ccc577f7df9c486000000000000000000000000000000000000000000000000000000005f5260045ffd5b6117ee92611c4361182392611ce0946110f8565b90611bb9565b7f484ab7df000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fff00000000000000000000000000000000000000000000000000000000000000611d388261114c565b60f81b1690611d4c6116f160208301610c2f565b907fffffffff0000000000000000000000000000000000000000000000000000000060408201357fffffffffffffffff000000000000000000000000000000000000000000000000611da361174860608601610abd565b611db26116f160808701610c2f565b928460c060a0880135970135976040519960208b0152166021890152602588015216604586015216604d84015260518301526071820152607181526117ee609182610190565b9293906101008511611e55579391905f925b808410611e1957505050501490565b90919294611e288683866115f8565b3583871c60011615611e4a575f526020525b600160405f209501929190611e0a565b905f52602052611e3a565b7f5e862a8a000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f6020604051611e8c8161016f565b6060815201528060041c600f8216155f14611ed25760ff5f5b168101809111610b2f57611eb89061157c565b9060405191611ec8604084610190565b8252602082015290565b60ff6001611ea5565b939291938151611eea86612253565b14801590611f93575b611f6b57611f01825161157c565b945f91825b858110611f1557505050505050565b611f208385846126f3565b611f2a818761221f565b158015611f5b575b611f5057600191611f4483928b6126a9565b019301945b9492611f06565b509260010194611f49565b50611f66818a61221f565b611f32565b7f5f64a3e0000000000000000000000000000000000000000000000000000000005f5260045ffd5b50611f9e8583612264565b8311611ef3565b8054821015611608575f5260205f2001905f90565b60041b90610ff060f0831692168203610b2f57565b60095481101561200a578060041c906008548210156116085760ff612004600f61ffff9460085f5260205f2001549316611fba565b161c1690565b7f4e23d035000000000000000000000000000000000000000000000000000000005f5260045ffd5b60055481101561200a578060041c906004548210156116085760ff612004600f61ffff9460045f5260205f2001549316611fba565b61ffff8060018301169116811061207b5790565b5061ffff90565b60095481101561200a57600f8160041c911660ff6120b261ffff80836120a786611fba565b161b19951692611fba565b161b916008548210156116085760085f8190527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee38301546101e0949216909117916120fc91611fa5565b9091907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83549160031b92831b921b1916179055565b9060055482101561200a57600f8260041c921660ff61216361ffff808361215886611fba565b161b19941692611fba565b161b906004548310156116085760045f528260205f20015416176004548210156116085760045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90910155565b90916117ee9493926040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000602082019260601b168252601481526121f8603482610190565b5190206001830154925460801c9261270d565b80518210156116085760209160051b010190565b60019160ff61223384938360081c9061220b565b5191161c161490565b916117ee939161224b936127f5565b9190916128c4565b60ff8101809111610b2f5760081c90565b919091821580156125e0575b6125da575f9161228a6122838560081c90565b9460ff1690565b5f935b858510806125d0575b15612413576001906122a8868661220b565b517f5555555555555555555555555555555555555555555555555555555555555555806122d58360011c90565b169116017f3333333333333333333333333333333333333333333333333333333333333333806123058360021c90565b169116017f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f806123358360041c90565b169116017eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff806123648360081c90565b169116017dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff806123928360101c90565b169116017bffffffff00000000ffffffff00000000ffffffff00000000ffffffff806123be8360201c90565b1691160177ffffffffffffffff0000000000000000ffffffffffffffff806123e68360401c90565b169116016fffffffffffffffffffffffffffffffff806124068360801c90565b169116010194019361228d565b93509093811515806125c6575b61242b575b50505090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600161245a931b019261220b565b51167f5555555555555555555555555555555555555555555555555555555555555555806124888360011c90565b169116017f3333333333333333333333333333333333333333333333333333333333333333806124b88360021c90565b169116017f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f806124e88360041c90565b169116017eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff806125178360081c90565b169116017dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff806125458360101c90565b169116017bffffffff00000000ffffffff00000000ffffffff00000000ffffffff806125718360201c90565b1691160177ffffffffffffffff0000000000000000ffffffffffffffff806125998360401c90565b169116016fffffffffffffffffffffffffffffffff806125b98360801c90565b16911601015f8080612425565b5082518110612420565b5083518510612296565b505f9150565b50805115612270565b600381116125f45790565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818111610b2f57600390048103908111610b2f5790565b9061263882612a4d565b8301809311610b2f5761264a90612a4d565b8060011b907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811603610b2f5780600101600111610b2f57600190830101809211610b2f57612698906125e9565b808210156126a4575090565b905090565b906126cc908060081c90600160ff6126c1848761220b565b5192161b179261220b565b52565b906126cc908060081c90600160ff6126e7848761220b565b5192161b19169261220b565b8215612706575f5260205260405f200690565b5050505f90565b939492808310156127eb579490929192945f915b8383106127315750505050501490565b909192939560018088161480156127df575b1561279c576127538486846115f8565b355f526020526001806127907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60405f20995b831c950160011c90565b01930191939290612721565b6127a78486846115f8565b35905f526020526001806127907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60405f2099612786565b50826001880114612743565b5050505050505f90565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161287d576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15612872575f5173ffffffffffffffffffffffffffffffffffffffff81161561286a57905f90565b505f90600190565b6040513d5f823e3d90fd5b505050505f90600390565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600511156128bf57565b612888565b6128cd816128b5565b806128d55750565b6128de816128b5565b60018103612945576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606490fd5b61294e816128b5565b600281036129b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606490fd5b806129c16003926128b5565b146129c857565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608490fd5b805f91612a5a8260801c90565b80612b58575b5080612a6f612ad19260401c90565b80612b4b575b50612a808160201c90565b80612b3e575b50612a918160101c90565b80612b31575b50612aa28160081c90565b80612b24575b50612ab38160041c90565b80612b17575b50612ac48160021c90565b80612b0a575b5060011c90565b612b00575b612ae06001612ba8565b9081612af3575b50156117ee5760010190565b90506001821b105f612ae7565b9060010190612ad6565b600291509301925f612aca565b600491509301925f612ab9565b600891509301925f612aa8565b601091509301925f612a97565b602091509301925f612a86565b604091509301925f612a75565b608093509050612ad1612a60565b63ffffffff8111612b805763ffffffff6117ee9116612bea565b7fe809999a000000000000000000000000000000000000000000000000000000005f5260045ffd5b60048110156128bf5760018091161490565b63ffffffff60029116019063ffffffff8211610b2f57565b63ffffffff60019116019063ffffffff8211610b2f57565b63ffffffff8116603f8111612c5257506117ee612c1a612c146106f39363fffffffc9060021b1690565b60ff1690565b604051928391602083017fff0000000000000000000000000000000000000000000000000000000000000060019260f81b1681520190565b613fff8111612cd357506117ee612c9b612c88612c81612c7c6106f39563fffffffc9060021b1690565b612bd2565b61ffff1690565b60ff61ff008260081b169160081c161790565b604051928391602083017fffff00000000000000000000000000000000000000000000000000000000000060029260f01b1681520190565b633fffffff10612d61576117ee612d29612cff612cfa6106f39463fffffffc9060021b1690565b612bba565b600881811b63ff00ff001691901c62ff00ff1617601081811b63ffff00001691901c61ffff161790565b604051928391602083017fffffffff0000000000000000000000000000000000000000000000000000000060049260e01b1681520190565b6106f3906117ee90600881811b63ff00ff001691901c62ff00ff1617601081811b63ffff00001691901c61ffff1617604051928391602083017fffffffff000000000000000000000000000000000000000000000000000000006005927f0300000000000000000000000000000000000000000000000000000000000000835260e01b166001820152019056fea26469706673582212207b0130e21be42133dbda81b4a66a95b0466f7b2c24e0dcbb014e624b44416e3764736f6c634300081c0033
Verified Source Code Full Match
Compiler: v0.8.28+commit.7893614a
EVM: cancun
Optimization: Yes (20000 runs)
BeefyClient.sol 651 lines
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> pragma solidity 0.8.28; import {ECDSA} from "openzeppelin/utils/cryptography/ECDSA.sol"; import {SubstrateMerkleProof} from "./utils/SubstrateMerkleProof.sol"; import {Bitfield} from "./utils/Bitfield.sol"; import {Uint16Array, createUint16Array} from "./utils/Uint16Array.sol"; import {Math} from "./utils/Math.sol"; import {MMRProof} from "./utils/MMRProof.sol"; import {ScaleCodec} from "./utils/ScaleCodec.sol"; /** * @title BeefyClient * * The BEEFY protocol is defined in https://eprint.iacr.org/2025/057.pdf. Higher level documentation * is available at https://docs.snowbridge.network/architecture/verification/polkadot. * * To submit new commitments, relayers must call the following methods sequentially: * 1. submitInitial: Setup the session for the interactive submission * 2. commitPrevRandao: Commit to a random seed for generating a validator subsampling * 3. createFinalBitfield: Generate the validator subsampling * 4. submitFinal: Complete submission after providing the request validator signatures * */ contract BeefyClient { using Math for uint16; using Math for uint256; /* Events */ /** * @dev Emitted when the MMR root is updated * @param mmrRoot the updated MMR root * @param blockNumber the beefy block number of the updated MMR root */ event NewMMRRoot(bytes32 mmrRoot, uint64 blockNumber); /** * @dev Emitted when a new ticket has been created * @param relayer The relayer who created the ticket * @param blockNumber the parent block number of the candidate MMR root */ event NewTicket(address relayer, uint64 blockNumber); /* Types */ /** * @dev The Commitment, with its payload, is the core thing we are trying to verify with * this contract. It contains an MMR root that commits to the polkadot history, including * past blocks and parachain blocks and can be used to verify both polkadot and parachain blocks. */ struct Commitment { // Relay chain block number uint32 blockNumber; // ID of the validator set that signed the commitment uint64 validatorSetID; // The payload of the new commitment in beefy justifications (in // our case, this is a new MMR root for all past polkadot blocks) PayloadItem[] payload; } /** * @dev Each PayloadItem is a piece of data signed by validators at a particular block. */ struct PayloadItem { // An ID that references a description of the data in the payload item. // Known payload ids can be found [upstream](https://github.com/paritytech/substrate/blob/fe1f8ba1c4f23931ae89c1ada35efb3d908b50f5/primitives/consensus/beefy/src/payload.rs#L27). bytes2 payloadID; // The contents of the payload item bytes data; } /** * @dev The ValidatorProof is a proof used to verify a commitment signature */ struct ValidatorProof { // The parity bit to specify the intended solution uint8 v; // The x component on the secp256k1 curve bytes32 r; // The challenge solution bytes32 s; // Leaf index of the validator address in the merkle tree uint256 index; // Validator address address account; // Merkle proof for the validator bytes32[] proof; } /** * @dev A ticket tracks working state for the interactive submission of new commitments */ struct Ticket { // The block number this ticket was issued uint64 blockNumber; // Length of the validator set that signed the commitment uint32 validatorSetLen; // The number of signatures required uint32 numRequiredSignatures; // The PREVRANDAO seed selected for this ticket session uint256 prevRandao; // Hash of a bitfield claiming which validators have signed bytes32 bitfieldHash; } /// @dev The MMRLeaf describes the leaf structure of the MMR struct MMRLeaf { // Version of the leaf type uint8 version; // Parent number of the block this leaf describes uint32 parentNumber; // Parent hash of the block this leaf describes bytes32 parentHash; // Validator set id that will be part of consensus for the next block uint64 nextAuthoritySetID; // Length of that validator set uint32 nextAuthoritySetLen; // Merkle root of all public keys in that validator set bytes32 nextAuthoritySetRoot; // Merkle root of all parachain headers in this block bytes32 parachainHeadsRoot; } /** * @dev The ValidatorSet describes a BEEFY validator set */ struct ValidatorSet { // Identifier for the set uint128 id; // Number of validators in the set uint128 length; // Merkle root of BEEFY validator addresses bytes32 root; } /** * @dev The ValidatorSetState describes a BEEFY validator set along with signature usage counters */ struct ValidatorSetState { // Identifier for the set uint128 id; // Number of validators in the set uint128 length; // Merkle root of BEEFY validator addresses bytes32 root; // Number of times a validator signature has been used Uint16Array usageCounters; } /* State */ /// @dev The latest verified MMR root bytes32 public latestMMRRoot; /// @dev The block number in the relay chain in which the latest MMR root was emitted uint64 public latestBeefyBlock; /// @dev State of the current validator set ValidatorSetState public currentValidatorSet; /// @dev State of the next validator set ValidatorSetState public nextValidatorSet; /// @dev Pending tickets for commitment submission mapping(bytes32 ticketID => Ticket) public tickets; /* Constants */ /** * @dev Beefy payload id for MMR Root payload items: * https://github.com/paritytech/substrate/blob/fe1f8ba1c4f23931ae89c1ada35efb3d908b50f5/primitives/consensus/beefy/src/payload.rs#L33 */ bytes2 public constant MMR_ROOT_ID = bytes2("mh"); /** * @dev Minimum delay in number of blocks that a relayer must wait between calling * submitInitial and commitPrevRandao. In production this should be set to MAX_SEED_LOOKAHEAD: * https://eth2book.info/altair/part3/config/preset#max_seed_lookahead */ uint256 public immutable randaoCommitDelay; /** * @dev after randaoCommitDelay is reached, relayer must * call commitPrevRandao within this number of blocks. * Without this expiration, relayers can roll the dice infinitely to get the subsampling * they desire. */ uint256 public immutable randaoCommitExpiration; /** * @dev The lower bound on the number of signatures required to validate a new commitment. Note * that the final number of signatures is calculated dynamically. */ uint256 public immutable minNumRequiredSignatures; /* Errors */ error InvalidBitfield(); error InvalidBitfieldLength(); error InvalidCommitment(); error InvalidMMRLeaf(); error InvalidMMRLeafProof(); error InvalidMMRRootLength(); error InvalidSignature(); error InvalidTicket(); error InvalidValidatorProof(); error InvalidValidatorProofLength(); error CommitmentNotRelevant(); error PrevRandaoAlreadyCaptured(); error PrevRandaoNotCaptured(); error StaleCommitment(); error TicketExpired(); error WaitPeriodNotOver(); constructor( uint256 _randaoCommitDelay, uint256 _randaoCommitExpiration, uint256 _minNumRequiredSignatures, uint64 _initialBeefyBlock, ValidatorSet memory _initialValidatorSet, ValidatorSet memory _nextValidatorSet ) { if (_nextValidatorSet.id != _initialValidatorSet.id + 1) { revert("invalid-constructor-params"); } randaoCommitDelay = _randaoCommitDelay; randaoCommitExpiration = _randaoCommitExpiration; minNumRequiredSignatures = _minNumRequiredSignatures; latestBeefyBlock = _initialBeefyBlock; currentValidatorSet.id = _initialValidatorSet.id; currentValidatorSet.length = _initialValidatorSet.length; currentValidatorSet.root = _initialValidatorSet.root; currentValidatorSet.usageCounters = createUint16Array(currentValidatorSet.length); nextValidatorSet.id = _nextValidatorSet.id; nextValidatorSet.length = _nextValidatorSet.length; nextValidatorSet.root = _nextValidatorSet.root; nextValidatorSet.usageCounters = createUint16Array(nextValidatorSet.length); } /* External Functions */ /** * @dev Begin submission of commitment * @param commitment contains the commitment signed by the validators * @param bitfield a bitfield claiming which validators have signed the commitment * @param proof a proof that a single validator from currentValidatorSet has signed the commitment */ function submitInitial( Commitment calldata commitment, uint256[] calldata bitfield, ValidatorProof calldata proof ) external { if (commitment.blockNumber <= latestBeefyBlock) { revert StaleCommitment(); } ValidatorSetState storage vset = currentValidatorSet; uint16 signatureUsageCount; if (commitment.validatorSetID == currentValidatorSet.id) { signatureUsageCount = currentValidatorSet.usageCounters.get(proof.index); currentValidatorSet.usageCounters.set( proof.index, signatureUsageCount.saturatingAdd(1) ); } else if (commitment.validatorSetID == nextValidatorSet.id) { signatureUsageCount = nextValidatorSet.usageCounters.get(proof.index); nextValidatorSet.usageCounters.set(proof.index, signatureUsageCount.saturatingAdd(1)); vset = nextValidatorSet; } else { revert InvalidCommitment(); } // Check if merkle proof is valid based on the validatorSetRoot and if proof is included in bitfield if ( !isValidatorInSet(vset, proof.account, proof.index, proof.proof) || !Bitfield.isSet(bitfield, proof.index) ) { revert InvalidValidatorProof(); } // Check if validatorSignature is correct, ie. check if it matches // the signature of senderPublicKey on the commitmentHash bytes32 commitmentHash = keccak256(encodeCommitment(commitment)); if (ECDSA.recover(commitmentHash, proof.v, proof.r, proof.s) != proof.account) { revert InvalidSignature(); } // For the initial submission, the supplied bitfield should claim that more than // two thirds of the validator set have sign the commitment if (bitfield.length != Bitfield.containerLength(vset.length) || Bitfield.countSetBits(bitfield, vset.length) < computeQuorum(vset.length)) { revert InvalidBitfield(); } tickets[createTicketID(msg.sender, commitmentHash)] = Ticket({ blockNumber: uint64(block.number), validatorSetLen: uint32(vset.length), numRequiredSignatures: uint32( computeNumRequiredSignatures( vset.length, signatureUsageCount, minNumRequiredSignatures ) ), prevRandao: 0, bitfieldHash: keccak256(abi.encodePacked(bitfield)) }); emit NewTicket(msg.sender, commitment.blockNumber); } /** * @dev Capture PREVRANDAO * @param commitmentHash contains the commitmentHash signed by the validators */ function commitPrevRandao(bytes32 commitmentHash) external { bytes32 ticketID = createTicketID(msg.sender, commitmentHash); Ticket storage ticket = tickets[ticketID]; if (ticket.blockNumber == 0) { revert InvalidTicket(); } if (ticket.prevRandao != 0) { revert PrevRandaoAlreadyCaptured(); } // relayer must wait `randaoCommitDelay` blocks if (block.number < ticket.blockNumber + randaoCommitDelay) { revert WaitPeriodNotOver(); } // relayer can capture within `randaoCommitExpiration` blocks if (block.number > ticket.blockNumber + randaoCommitDelay + randaoCommitExpiration) { delete tickets[ticketID]; revert TicketExpired(); } // Post-merge, the difficulty opcode now returns PREVRANDAO ticket.prevRandao = block.prevrandao; } /** * @dev Submit a commitment and leaf for final verification * @param commitment contains the full commitment that was used for the commitmentHash * @param bitfield claiming which validators have signed the commitment * @param proofs a struct containing the data needed to verify all validator signatures * @param leaf an MMR leaf provable using the MMR root in the commitment payload * @param leafProof an MMR leaf proof * @param leafProofOrder a bitfield describing the order of each item (left vs right) */ function submitFinal( Commitment calldata commitment, uint256[] calldata bitfield, ValidatorProof[] calldata proofs, MMRLeaf calldata leaf, bytes32[] calldata leafProof, uint256 leafProofOrder ) external { bytes32 commitmentHash = keccak256(encodeCommitment(commitment)); bytes32 ticketID = createTicketID(msg.sender, commitmentHash); validateTicket(ticketID, commitment, bitfield); bool is_next_session = false; ValidatorSetState storage vset = currentValidatorSet; if (commitment.validatorSetID == nextValidatorSet.id) { is_next_session = true; vset = nextValidatorSet; } else if (commitment.validatorSetID != currentValidatorSet.id) { revert InvalidCommitment(); } verifyCommitment(commitmentHash, ticketID, bitfield, vset, proofs); bytes32 newMMRRoot = ensureProvidesMMRRoot(commitment); if (is_next_session) { if (leaf.nextAuthoritySetID != nextValidatorSet.id + 1) { revert InvalidMMRLeaf(); } bool leafIsValid = MMRProof.verifyLeafProof( newMMRRoot, keccak256(encodeMMRLeaf(leaf)), leafProof, leafProofOrder ); if (!leafIsValid) { revert InvalidMMRLeafProof(); } currentValidatorSet = nextValidatorSet; nextValidatorSet.id = leaf.nextAuthoritySetID; nextValidatorSet.length = leaf.nextAuthoritySetLen; nextValidatorSet.root = leaf.nextAuthoritySetRoot; nextValidatorSet.usageCounters = createUint16Array(leaf.nextAuthoritySetLen); } latestMMRRoot = newMMRRoot; latestBeefyBlock = commitment.blockNumber; delete tickets[ticketID]; emit NewMMRRoot(newMMRRoot, commitment.blockNumber); } /** * @dev Verify that the supplied MMR leaf is included in the latest verified MMR root. * @param leafHash contains the merkle leaf to be verified * @param proof contains simplified mmr proof * @param proofOrder a bitfield describing the order of each item (left vs right) */ function verifyMMRLeafProof(bytes32 leafHash, bytes32[] calldata proof, uint256 proofOrder) external view returns (bool) { return MMRProof.verifyLeafProof(latestMMRRoot, leafHash, proof, proofOrder); } /** * @dev Helper to create an initial validator bitfield. * @param bitsToSet contains indexes of all signed validators, should be deduplicated * @param length of validator set */ function createInitialBitfield(uint256[] calldata bitsToSet, uint256 length) external pure returns (uint256[] memory) { if (length < bitsToSet.length) { revert InvalidBitfieldLength(); } return Bitfield.createBitfield(bitsToSet, length); } /** * @dev Helper to create a final bitfield, with subsampled validator selections * @param commitmentHash contains the commitmentHash signed by the validators * @param bitfield claiming which validators have signed the commitment */ function createFinalBitfield(bytes32 commitmentHash, uint256[] calldata bitfield) external view returns (uint256[] memory) { Ticket storage ticket = tickets[createTicketID(msg.sender, commitmentHash)]; if (ticket.bitfieldHash != keccak256(abi.encodePacked(bitfield))) { revert InvalidBitfield(); } return Bitfield.subsample( ticket.prevRandao, bitfield, ticket.validatorSetLen, ticket.numRequiredSignatures ); } /* Internal Functions */ // Creates a unique ticket ID for a new interactive prover-verifier session function createTicketID(address account, bytes32 commitmentHash) internal pure returns (bytes32 value) { assembly { mstore(0x00, account) mstore(0x20, commitmentHash) value := keccak256(0x0, 0x40) } } /** * @dev Calculates the number of required signatures for `submitFinal`. * @param validatorSetLen The length of the validator set * @param signatureUsageCount A counter of the number of times the validator signature was previously used in a call to `submitInitial` within the session. * @param minRequiredSignatures The minimum amount of signatures to verify */ // For more details on the calculation, read the following: // 1. https://docs.snowbridge.network/architecture/verification/polkadot#signature-sampling // 2. https://hackmd.io/9OedC7icR5m-in_moUZ_WQ function computeNumRequiredSignatures( uint256 validatorSetLen, uint256 signatureUsageCount, uint256 minRequiredSignatures ) internal pure returns (uint256) { // Start with the minimum number of signatures. uint256 numRequiredSignatures = minRequiredSignatures; // Add signatures based on the number of validators in the validator set. numRequiredSignatures += Math.log2(validatorSetLen, Math.Rounding.Ceil); // Add signatures based on the signature usage count. numRequiredSignatures += 1 + (2 * Math.log2(signatureUsageCount, Math.Rounding.Ceil)); // Never require more signatures than a 2/3 majority return Math.min(numRequiredSignatures, computeQuorum(validatorSetLen)); } /** * @dev Calculates majority required for quorum for a given number of validators. * @param numValidators The number of validators in the validator set. */ function computeQuorum(uint256 numValidators) internal pure returns (uint256) { if (numValidators > 3) { return numValidators - (numValidators - 1) / 3; } return numValidators; } /** * @dev Verify commitment using the supplied signature proofs */ function verifyCommitment( bytes32 commitmentHash, bytes32 ticketID, uint256[] calldata bitfield, ValidatorSetState storage vset, ValidatorProof[] calldata proofs ) internal view { Ticket storage ticket = tickets[ticketID]; // Verify that enough signature proofs have been supplied uint256 numRequiredSignatures = ticket.numRequiredSignatures; if (proofs.length != numRequiredSignatures) { revert InvalidValidatorProofLength(); } // Generate final bitfield indicating which validators need to be included in the proofs. uint256[] memory finalbitfield = Bitfield.subsample(ticket.prevRandao, bitfield, vset.length, numRequiredSignatures); for (uint256 i = 0; i < proofs.length; i++) { ValidatorProof calldata proof = proofs[i]; // Check that validator is actually in a validator set if (!isValidatorInSet(vset, proof.account, proof.index, proof.proof)) { revert InvalidValidatorProof(); } // Check that validator is in bitfield if (!Bitfield.isSet(finalbitfield, proof.index)) { revert InvalidValidatorProof(); } // Check that validator signed the commitment if (ECDSA.recover(commitmentHash, proof.v, proof.r, proof.s) != proof.account) { revert InvalidSignature(); } // Ensure no validator can appear more than once in bitfield Bitfield.unset(finalbitfield, proof.index); } } // Ensure that the commitment provides a new MMR root function ensureProvidesMMRRoot(Commitment calldata commitment) internal pure returns (bytes32) { for (uint256 i = 0; i < commitment.payload.length; i++) { if (commitment.payload[i].payloadID == MMR_ROOT_ID) { if (commitment.payload[i].data.length != 32) { revert InvalidMMRRootLength(); } else { return bytes32(commitment.payload[i].data); } } } revert CommitmentNotRelevant(); } function encodeCommitment(Commitment calldata commitment) internal pure returns (bytes memory) { return bytes.concat( encodeCommitmentPayload(commitment.payload), ScaleCodec.encodeU32(commitment.blockNumber), ScaleCodec.encodeU64(commitment.validatorSetID) ); } function encodeCommitmentPayload(PayloadItem[] calldata items) internal pure returns (bytes memory) { bytes memory payload = ScaleCodec.checkedEncodeCompactU32(items.length); for (uint256 i = 0; i < items.length; i++) { payload = bytes.concat( payload, items[i].payloadID, ScaleCodec.checkedEncodeCompactU32(items[i].data.length), items[i].data ); } return payload; } function encodeMMRLeaf(MMRLeaf calldata leaf) internal pure returns (bytes memory) { return bytes.concat( ScaleCodec.encodeU8(leaf.version), ScaleCodec.encodeU32(leaf.parentNumber), leaf.parentHash, ScaleCodec.encodeU64(leaf.nextAuthoritySetID), ScaleCodec.encodeU32(leaf.nextAuthoritySetLen), leaf.nextAuthoritySetRoot, leaf.parachainHeadsRoot ); } /** * @dev Checks if a validators address is a member of the merkle tree * @param vset The validator set * @param account The address of the validator to check for inclusion in `vset`. * @param index The leaf index of the account in the merkle tree of validator set addresses. * @param proof Merkle proof required for validation of the address * @return true if the validator is in the set */ function isValidatorInSet( ValidatorSetState storage vset, address account, uint256 index, bytes32[] calldata proof ) internal view returns (bool) { bytes32 hashedLeaf = keccak256(abi.encodePacked(account)); return SubstrateMerkleProof.verify(vset.root, hashedLeaf, index, vset.length, proof); } /** * @dev Basic validation of a ticket for submitFinal */ function validateTicket( bytes32 ticketID, Commitment calldata commitment, uint256[] calldata bitfield ) internal view { Ticket storage ticket = tickets[ticketID]; if (ticket.blockNumber == 0) { // submitInitial hasn't been called yet revert InvalidTicket(); } if (ticket.prevRandao == 0) { // commitPrevRandao hasn't been called yet revert PrevRandaoNotCaptured(); } if (commitment.blockNumber <= latestBeefyBlock) { // ticket is obsolete revert StaleCommitment(); } if (ticket.bitfieldHash != keccak256(abi.encodePacked(bitfield))) { // The provided claims bitfield isn't the same one that was // passed to submitInitial revert InvalidBitfield(); } } }
ECDSA.sol 217 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}
SubstrateMerkleProof.sol 64 lines
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> pragma solidity 0.8.28; // Used to verify merkle proofs generated by https://github.com/paritytech/substrate/tree/master/utils/binary-merkle-tree library SubstrateMerkleProof { /** * @notice Verify that a specific leaf element is part of the Merkle Tree at a specific position in the tree * * The tree would have been constructed using * https://paritytech.github.io/substrate/master/binary_merkle_tree/fn.merkle_root.html * * This implementation adapted from * https://paritytech.github.io/substrate/master/binary_merkle_tree/fn.verify_proof.html * * @param root the root of the merkle tree * @param leaf the leaf which needs to be proven * @param position the position of the leaf, index starting with 0 * @param width the width or number of leaves in the tree * @param proof the array of proofs to help verify the leaf's membership, ordered from leaf to root * @return a boolean value representing the success or failure of the operation */ function verify( bytes32 root, bytes32 leaf, uint256 position, uint256 width, bytes32[] calldata proof ) internal pure returns (bool) { if (position >= width) { return false; } return root == computeRoot(leaf, position, width, proof); } function computeRoot(bytes32 leaf, uint256 position, uint256 width, bytes32[] calldata proof) internal pure returns (bytes32) { bytes32 node = leaf; unchecked { for (uint256 i = 0; i < proof.length; i++) { if (position & 1 == 1 || position + 1 == width) { node = efficientHash(proof[i], node); } else { node = efficientHash(node, proof[i]); } position = position >> 1; width = ((width - 1) >> 1) + 1; } return node; } } function efficientHash(bytes32 a, bytes32 b) internal pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
Bitfield.sol 216 lines
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> pragma solidity 0.8.28; import {Bits} from "./Bits.sol"; library Bitfield { using Bits for uint256; error InvalidSamplingParams(); /** * @dev Constants used to efficiently calculate the hamming weight of a bitfield. See * https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation for an explanation of those constants. */ uint256 internal constant M1 = 0x5555555555555555555555555555555555555555555555555555555555555555; uint256 internal constant M2 = 0x3333333333333333333333333333333333333333333333333333333333333333; uint256 internal constant M4 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f; uint256 internal constant M8 = 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff; uint256 internal constant M16 = 0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff; uint256 internal constant M32 = 0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff; uint256 internal constant M64 = 0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff; uint256 internal constant M128 = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff; uint256 internal constant ONE = uint256(1); /** * @dev Core subsampling algorithm. Draws a random number, derives an index in the bitfield, * and sets the bit if it is in the `priorBitfield` and not yet set. Repeats that `n` times. * @param seed Source of randomness for selecting validator signatures. * @param priorBitfield Bitfield indicating which validators claim to have signed the commitment. * @param priorBitfieldSize Number of bits in priorBitfield Must be <= priorBitfield.length * 256. * @param n Number of unique bits in priorBitfield that must be set in the output. * Must be <= number of set bits in priorBitfield. */ function subsample( uint256 seed, uint256[] memory priorBitfield, uint256 priorBitfieldSize, uint256 n ) internal pure returns (uint256[] memory outputBitfield) { if (priorBitfield.length != Bitfield.containerLength(priorBitfieldSize) || n > countSetBits(priorBitfield, priorBitfieldSize)) { revert InvalidSamplingParams(); } outputBitfield = new uint256[](priorBitfield.length); uint256 found = 0; for (uint256 i = 0; found < n;) { uint256 index = makeIndex(seed, i, priorBitfieldSize); // require randomly selected bit to be set in priorBitfield and not yet set in bitfield if (!isSet(priorBitfield, index) || isSet(outputBitfield, index)) { unchecked { i++; } continue; } set(outputBitfield, index); unchecked { found++; i++; } } } /** * @dev Helper to create a bitfield. */ function createBitfield(uint256[] calldata bitsToSet, uint256 length) internal pure returns (uint256[] memory bitfield) { bitfield = new uint256[](containerLength(length)); for (uint256 i = 0; i < bitsToSet.length; i++) { set(bitfield, bitsToSet[i]); } return bitfield; } /** * @notice Calculates the number of set bits by using the hamming weight of the bitfield. * The algorithm below is implemented after https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation. * Further improvements are possible, see the article above. */ function countSetBits(uint256[] memory self) internal pure returns (uint256) { unchecked { uint256 count = 0; for (uint256 i = 0; i < self.length; i++) { uint256 x = self[i]; x = (x & M1) + ((x >> 1) & M1); //put count of each 2 bits into those 2 bits x = (x & M2) + ((x >> 2) & M2); //put count of each 4 bits into those 4 bits x = (x & M4) + ((x >> 4) & M4); //put count of each 8 bits into those 8 bits x = (x & M8) + ((x >> 8) & M8); //put count of each 16 bits into those 16 bits x = (x & M16) + ((x >> 16) & M16); //put count of each 32 bits into those 32 bits x = (x & M32) + ((x >> 32) & M32); //put count of each 64 bits into those 64 bits x = (x & M64) + ((x >> 64) & M64); //put count of each 128 bits into those 128 bits x = (x & M128) + ((x >> 128) & M128); //put count of each 256 bits into those 256 bits count += x; } return count; } } /** * @notice Calculates the number of set bits in the first `maxBits` bits of the bitfield. * This is a bounded variant of `countSetBits` that only counts bits within the specified range. * * @dev Example usage: * If a bitfield has bits set at positions [0, 5, 10, 256, 300]: * - countSetBits(bitfield, 11) returns 3 (bits 0, 5, 10) * - countSetBits(bitfield, 257) returns 4 (bits 0, 5, 10, 256) * - countSetBits(bitfield, 1000) returns 5 (all bits) * * @param self The bitfield to count bits in * @param maxBits The maximum number of bits to count (counting from bit 0) * @return count The number of set bits in the first `maxBits` positions */ function countSetBits(uint256[] memory self, uint256 maxBits) internal pure returns (uint256) { if (maxBits == 0 || self.length == 0) { return 0; } unchecked { uint256 count = 0; uint256 fullElements = maxBits / 256; uint256 remainingBits = maxBits % 256; // Count bits in full 256-bit elements for (uint256 i = 0; i < fullElements && i < self.length; i++) { uint256 x = self[i]; x = (x & M1) + ((x >> 1) & M1); //put count of each 2 bits into those 2 bits x = (x & M2) + ((x >> 2) & M2); //put count of each 4 bits into those 4 bits x = (x & M4) + ((x >> 4) & M4); //put count of each 8 bits into those 8 bits x = (x & M8) + ((x >> 8) & M8); //put count of each 16 bits into those 16 bits x = (x & M16) + ((x >> 16) & M16); //put count of each 32 bits into those 32 bits x = (x & M32) + ((x >> 32) & M32); //put count of each 64 bits into those 64 bits x = (x & M64) + ((x >> 64) & M64); //put count of each 128 bits into those 128 bits x = (x & M128) + ((x >> 128) & M128); //put count of each 256 bits into those 256 bits count += x; } // Count bits in the partial element (if any) if (remainingBits > 0 && fullElements < self.length) { uint256 mask = (ONE << remainingBits) - 1; uint256 x = self[fullElements] & mask; x = (x & M1) + ((x >> 1) & M1); x = (x & M2) + ((x >> 2) & M2); x = (x & M4) + ((x >> 4) & M4); x = (x & M8) + ((x >> 8) & M8); x = (x & M16) + ((x >> 16) & M16); x = (x & M32) + ((x >> 32) & M32); x = (x & M64) + ((x >> 64) & M64); x = (x & M128) + ((x >> 128) & M128); count += x; } return count; } } function isSet(uint256[] memory self, uint256 index) internal pure returns (bool) { uint256 element = index >> 8; return self[element].bit(uint8(index)) == 1; } function set(uint256[] memory self, uint256 index) internal pure { uint256 element = index >> 8; self[element] = self[element].setBit(uint8(index)); } function unset(uint256[] memory self, uint256 index) internal pure { uint256 element = index >> 8; self[element] = self[element].clearBit(uint8(index)); } function makeIndex(uint256 seed, uint256 iteration, uint256 length) internal pure returns (uint256 index) { // Handle case where length is 0 to prevent infinite loop in subsample if (length == 0) { return 0; } assembly { mstore(0x00, seed) mstore(0x20, iteration) index := mod(keccak256(0x00, 0x40), length) } } // Calculate length of uint256 bitfield array based on rounding up to number of uint256 needed function containerLength(uint256 bitfieldSize) internal pure returns (uint256) { return (bitfieldSize + 255) / 256; } }
Uint16Array.sol 93 lines
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> pragma solidity 0.8.28; /** * @title A utility library for 16 bit counters packed in 256 bit array. * @dev The BeefyClient needs to store a count of how many times a validators signature is used. In solidity * a uint16 would take up as much space as a uin256 in storage, making storing counters for 1000 validators * expensive in terms of gas. The BeefyClient only needs 16 bits per counter. This library allows us to pack * 16 uint16 into a single uint256 and save 16x storage. * * Layout of 32 counters (2 uint256) * We store all counts in a single large uint256 array and convert from index from the logical uint16 array * to the physical uint256 array. * * 0 1 2 * uint256[] |-- -- -- -- -- -- -- -- -- -- -- -- YY -- -- --|-- -- -- -- -- -- XX -- -- -- -- -- -- -- -- --| * uint16[] |--|--|--|--|--|--|--|--|--|--|--|--|YY|--|--|--|--|--|--|--|--|--|XX|--|--|--|--|--|--|--|--|--| * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 * * Logical Index Layout * We use the first 4 * |-------...---------|----| * 256 4 0 * ^index ^bit-index * * In the above table counter YY is at logical index 12 in the uint16 array. It will convert to a physical * index of 0 in the physical uint256 array and then to bit-index of 192 to 207 of that uint256. In the * above table counter XX is at logical index 22. It will convert to a physical index of 1 in the array and * then to bit-index 96 to 111 of uint256[1]. */ using {get, set} for Uint16Array global; error IndexOutOfBounds(); /** * @dev stores the backing array and the length. */ struct Uint16Array { uint256[] data; uint256 length; } /** * @dev Creates a new counter which can store at least `length` counters. * @param length The amount of counters. */ function createUint16Array(uint256 length) pure returns (Uint16Array memory) { // create space for `length` elements and round up if needed. uint256 bufferLength = length / 16 + (length % 16 == 0 ? 0 : 1); return Uint16Array({data: new uint256[](bufferLength), length: length}); } /** * @dev Gets the counter at the logical index * @param self The array. * @param index The logical index. */ function get(Uint16Array storage self, uint256 index) view returns (uint16) { if (index >= self.length) { revert IndexOutOfBounds(); } // Right-shift the index by 4. This truncates the first 4 bits (bit-index) leaving us with the index // into the array. uint256 element = index >> 4; // Mask out the first 4 bits of the logical index to give us the bit-index. uint8 inside = uint8(index) & 0x0F; // find the element in the array, shift until its bit index and mask to only take the first 16 bits. return uint16((self.data[element] >> (16 * inside)) & 0xFFFF); } /** * @dev Sets the counter at the logical index. * @param self The array. * @param index The logical index of the counter in the array. * @param value The value to set the counter to. */ function set(Uint16Array storage self, uint256 index, uint16 value) { if (index >= self.length) { revert IndexOutOfBounds(); } // Right-shift the index by 4. This truncates the first 4 bits (bit-index) leaving us with the index // into the array. uint256 element = index >> 4; // Mask out the first 4 bytes of the logical index to give us the bit-index. uint8 inside = uint8(index) & 0x0F; // Create a zero mask which will clear the existing value at the bit-index. uint256 zero = ~(uint256(0xFFFF) << (16 * inside)); // Shift the value to the bit index. uint256 shiftedValue = uint256(value) << (16 * inside); // Take the element, apply the zero mask to clear the existing value, and then apply the shifted value with bitwise or. self.data[element] = self.data[element] & zero | shiftedValue; }
Math.sol 117 lines
// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2023 OpenZeppelin // SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> // Code from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol pragma solidity 0.8.28; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } /** * @dev Safely adds two unsigned 16-bit integers, preventing overflow by saturating to max uint16. */ function saturatingAdd(uint16 a, uint16 b) internal pure returns (uint16) { unchecked { uint16 c = a + b; if (c < a) { return 0xFFFF; } return c; } } /** * @dev Safely subtracts two unsigned 256-bit integers, preventing overflow by saturating to min uint256. */ function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { if (b >= a) { return 0; } return a - b; } } }
MMRProof.sol 53 lines
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> pragma solidity 0.8.28; library MMRProof { error ProofSizeExceeded(); uint256 internal constant MAXIMUM_PROOF_SIZE = 256; /** * @dev Verify inclusion of a leaf in an MMR * @param root MMR root hash * @param leafHash leaf hash * @param proof an array of hashes * @param proofOrder a bitfield describing the order of each item (left vs right) */ function verifyLeafProof( bytes32 root, bytes32 leafHash, bytes32[] calldata proof, uint256 proofOrder ) internal pure returns (bool) { // Size of the proof is bounded, since `proofOrder` can only contain `MAXIMUM_PROOF_SIZE` orderings. if (proof.length > MAXIMUM_PROOF_SIZE) { revert ProofSizeExceeded(); } bytes32 acc = leafHash; for (uint256 i = 0; i < proof.length; i++) { acc = hashPairs(acc, proof[i], (proofOrder >> i) & 1); } return root == acc; } function hashPairs(bytes32 x, bytes32 y, uint256 order) internal pure returns (bytes32 value) { assembly { switch order case 0 { mstore(0x00, x) mstore(0x20, y) } default { mstore(0x00, y) mstore(0x20, x) } value := keccak256(0x0, 0x40) } } }
ScaleCodec.sol 201 lines
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> pragma solidity 0.8.28; library ScaleCodec { error UnsupportedCompactEncoding(); uint256 internal constant MAX_COMPACT_ENCODABLE_UINT = 2 ** 30 - 1; // Sources: // * https://ethereum.stackexchange.com/questions/15350/how-to-convert-an-bytes-to-address-in-solidity/50528 // * https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel function reverse256(uint256 input) internal pure returns (uint256 v) { v = input; // swap bytes v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32) | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32); // swap 8-byte long pairs v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64) | ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64); // swap 16-byte long pairs v = (v >> 128) | (v << 128); } function reverse128(uint128 input) internal pure returns (uint128 v) { v = input; // swap bytes v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000) >> 32) | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF) << 32); // swap 8-byte long pairs v = (v >> 64) | (v << 64); } function reverse64(uint64 input) internal pure returns (uint64 v) { v = input; // swap bytes v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = (v >> 32) | (v << 32); } function reverse32(uint32 input) internal pure returns (uint32 v) { v = input; // swap bytes v = ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8); // swap 2-byte long pairs v = (v >> 16) | (v << 16); } function reverse16(uint16 input) internal pure returns (uint16 v) { v = input; // swap bytes v = (v >> 8) | (v << 8); } function encodeU256(uint256 input) internal pure returns (bytes32) { return bytes32(reverse256(input)); } function encodeU128(uint128 input) internal pure returns (bytes16) { return bytes16(reverse128(input)); } function encodeU64(uint64 input) internal pure returns (bytes8) { return bytes8(reverse64(input)); } function encodeU32(uint32 input) internal pure returns (bytes4) { return bytes4(reverse32(input)); } function encodeU16(uint16 input) internal pure returns (bytes2) { return bytes2(reverse16(input)); } function encodeU8(uint8 input) internal pure returns (bytes1) { return bytes1(input); } // Supports compact encoding of integers in [0, uint32.MAX] function encodeCompactU32(uint32 value) internal pure returns (bytes memory) { if (value <= 2 ** 6 - 1) { // add single byte flag return abi.encodePacked(uint8(value << 2)); } else if (value <= 2 ** 14 - 1) { // add two byte flag and create little endian encoding return abi.encodePacked(ScaleCodec.reverse16(uint16(((value << 2) + 1)))); } else if (value <= 2 ** 30 - 1) { // add four byte flag and create little endian encoding return abi.encodePacked(ScaleCodec.reverse32(uint32((value << 2)) + 2)); } else { return abi.encodePacked(uint8(3), ScaleCodec.reverse32(value)); } } function encodeCompactU128(uint128 value) internal pure returns (bytes memory) { // 1) up to 2^6 - 1 if (value <= 63) { // single byte = (value << 2) // (lowest two bits = 00) return abi.encodePacked(uint8(value << 2)); } // 2) up to 2^14 - 1 if (value <= 0x3FFF) { // two bytes = (value << 2) + 0x01 // (lowest two bits = 01) uint16 encoded = uint16(value << 2) | 0x01; // We must store it in little-endian return abi.encodePacked(reverse16(encoded)); } // 3) up to 2^30 - 1 if (value <= 0x3FFF_FFFF) { // four bytes = (value << 2) + 0x02 // (lowest two bits = 10) uint32 encoded = (uint32(value) << 2) | 0x02; return abi.encodePacked(reverse32(encoded)); } // 4) otherwise // big integer => prefix + little-endian bytes (no leading zeros) // prefix = 0x03 + ((numValueBytes - 4) << 2) // where numValueBytes is how many bytes needed to represent `value`. bytes memory littleEndian = _toLittleEndianNoLeadingZeros(value); uint8 len = uint8(littleEndian.length); // # of bytes needed // Substrate: prefix's lower 2 bits = 0b11, // the remaining upper bits = (len - 4). // Combined: prefix = 0x03 + ((len - 4) << 2). uint8 prefix = ((len - 4) << 2) | 0x03; // Concatenate prefix + actual bytes return abi.encodePacked(prefix, littleEndian); } // Convert `value` into a little-endian byte array with no leading zeros. // (Leading zeros in LE = trailing zeros in big-endian.) function _toLittleEndianNoLeadingZeros(uint128 value) private pure returns (bytes memory) { // Even if value=0, that case is handled above in smaller branches, // but let's just handle it gracefully anyway: if (value == 0) { return hex"00"; } // Temporarily build up to 16 bytes in a buffer. bytes memory buf = new bytes(16); uint128 current = value; uint8 i = 0; while (current != 0) { buf[i] = bytes1(uint8(current & 0xFF)); current >>= 8; unchecked { i++; } } // i is now the actual number of bytes used // Copy them into a new array of the correct size bytes memory out = new bytes(i); for (uint8 j = 0; j < i; j++) { out[j] = buf[j]; } return out; } function checkedEncodeCompactU32(uint256 value) internal pure returns (bytes memory) { if (value > type(uint32).max) { revert UnsupportedCompactEncoding(); } return encodeCompactU32(uint32(value)); } }
Strings.sol 85 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
Bits.sol 117 lines
// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> // Code from https://github.com/ethereum/solidity-examples pragma solidity 0.8.28; library Bits { uint256 internal constant ONE = uint256(1); uint256 internal constant ONES = type(uint256).max; // Sets the bit at the given 'index' in 'self' to '1'. // Returns the modified value. function setBit(uint256 self, uint8 index) internal pure returns (uint256) { return self | (ONE << index); } // Sets the bit at the given 'index' in 'self' to '0'. // Returns the modified value. function clearBit(uint256 self, uint8 index) internal pure returns (uint256) { return self & ~(ONE << index); } // Sets the bit at the given 'index' in 'self' to: // '1' - if the bit is '0' // '0' - if the bit is '1' // Returns the modified value. function toggleBit(uint256 self, uint8 index) internal pure returns (uint256) { return self ^ (ONE << index); } // Get the value of the bit at the given 'index' in 'self'. function bit(uint256 self, uint8 index) internal pure returns (uint8) { return uint8((self >> index) & 1); } // Check if the bit at the given 'index' in 'self' is set. // Returns: // 'true' - if the value of the bit is '1' // 'false' - if the value of the bit is '0' function bitSet(uint256 self, uint8 index) internal pure returns (bool) { return (self >> index) & 1 == 1; } // Checks if the bit at the given 'index' in 'self' is equal to the corresponding // bit in 'other'. // Returns: // 'true' - if both bits are '0' or both bits are '1' // 'false' - otherwise function bitEqual(uint256 self, uint256 other, uint8 index) internal pure returns (bool) { return ((self ^ other) >> index) & 1 == 0; } // Get the bitwise NOT of the bit at the given 'index' in 'self'. function bitNot(uint256 self, uint8 index) internal pure returns (uint8) { return uint8(1 - ((self >> index) & 1)); } // Computes the bitwise AND of the bit at the given 'index' in 'self', and the // corresponding bit in 'other', and returns the value. function bitAnd(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) { return uint8(((self & other) >> index) & 1); } // Computes the bitwise OR of the bit at the given 'index' in 'self', and the // corresponding bit in 'other', and returns the value. function bitOr(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) { return uint8(((self | other) >> index) & 1); } // Computes the bitwise XOR of the bit at the given 'index' in 'self', and the // corresponding bit in 'other', and returns the value. function bitXor(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) { return uint8(((self ^ other) >> index) & 1); } // Gets 'numBits' consecutive bits from 'self', starting from the bit at 'startIndex'. // Returns the bits as a 'uint'. // Requires that: // - '0 < numBits <= 256' // - 'startIndex < 256' // - 'numBits + startIndex <= 256' function bits(uint256 self, uint8 startIndex, uint16 numBits) internal pure returns (uint256) { require(0 < numBits && startIndex < 256 && startIndex + numBits <= 256, "out of bounds"); return (self >> startIndex) & (ONES >> (256 - numBits)); } // Computes the index of the highest bit set in 'self'. // Returns the highest bit set as an 'uint8'. // Requires that 'self != 0'. function highestBitSet(uint256 self) internal pure returns (uint8 highest) { require(self != 0, "should not be zero"); uint256 val = self; for (uint8 i = 128; i >= 1; i >>= 1) { if (val & (((ONE << i) - 1) << i) != 0) { highest += i; val >>= i; } } } // Computes the index of the lowest bit set in 'self'. // Returns the lowest bit set as an 'uint8'. // Requires that 'self != 0'. function lowestBitSet(uint256 self) internal pure returns (uint8 lowest) { require(self != 0, "should not be zero"); uint256 val = self; for (uint8 i = 128; i >= 1; i >>= 1) { if (val & ((ONE << i) - 1) == 0) { lowest += i; val >>= i; } } } }
Math.sol 339 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
SignedMath.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
Read Contract
MMR_ROOT_ID 0x0a7c8faa → bytes2
createFinalBitfield 0x8ab81d13 → uint256[]
createInitialBitfield 0x5da57fe9 → uint256[]
currentValidatorSet 0x2cdea717 → uint128, uint128, bytes32, tuple
latestBeefyBlock 0x66ae69a0 → uint64
latestMMRRoot 0x41c9634e → bytes32
minNumRequiredSignatures 0x6f55bd32 → uint256
nextValidatorSet 0x36667513 → uint128, uint128, bytes32, tuple
randaoCommitDelay 0x591d99ee → uint256
randaoCommitExpiration 0xad209a9b → uint256
tickets 0xdf0dd0d5 → uint64, uint32, uint32, uint256, bytes32
verifyMMRLeafProof 0xa401662b → bool
Write Contract 3 functions
These functions modify contract state and require a wallet transaction to execute.
commitPrevRandao 0xa77cf3d2
bytes32 commitmentHash
submitFinal 0xa23da32d
tuple commitment
uint256[] bitfield
tuple[] proofs
tuple leaf
bytes32[] leafProof
uint256 leafProofOrder
submitInitial 0x07078f4d
tuple commitment
uint256[] bitfield
tuple proof
Recent Transactions
No transactions found for this address