IntegrityProof
Proof of Bundle Integrity allows bundle senders to express exact slot he/she wants his/her bundle to be placed on a block and trustlessly prove whether a block builder has tempered with the bundle leveraging zero knowledge proof
Screenshots




Problem Statement
Goalsprove that bundle has not been tempered withenable trustless slot selectionRequirementsA challenger should be able to prove the block builder has tempered with the bundleBundle sender should not be able to game the systemBlock builder should not be able to game the systemA bundle sender should be able to express slot preference to a block builderDescriptionThere will be only one contract, call itproofOfIntegrity.solWhen new bundle is submitted, a sender should include a tx calling thestamp()function of theproofOfIntegrity.solcontractstamp(Txs tx, uint blockNumber, uint bundleIndex)take in all of the transactions in a bundle.Txsis an array that takes in a contract definedTransactiontype i.e.struct Transactionthe function calculate the hash usingkeccak256, count the tx number in the bundle, and useblockNumberandmsg.senderto update to mappingsmapping(address => (mapping(uint = > uint))) public bundleSizeMapthis is a mappingsenderAddress⇒blockNumber⇒bundleSizemapping(address => (mapping(uint => bytes))) public bundleHashthis is a mappingsenderAddress⇒blockNumber⇒bundleHashAdditionally, function will update another mappingmapping(address => (mapping uint => uint)) bundleIndexMapthis is a mappingsenderAddress⇒blockNumber⇒bundleIndexNote:bundleIndexis to express the slot number a bundle wants to be placed in the block. The top slot a bundle can express is 2, since the slot 1 will always be taken by theverifyBundle()function invoked by the block builder(more on this later)Then, the function will check the value in themapping(address => (mapping uint => bool)) bundleVerificationMap. If the endingboolis the default value0, then the function reverts with logbundleVerificationMap not updatedNote: the reason why it needs to revert is because it forces the block builder to insert averifyBundle()transaction call on the top of the bundle. This will also let the block builder know that the sender has requested that he/she wants the bundle to be later subject to proof-of-integritythis is a mappingsenderAddress⇒blockNumber⇒verifiedthis mapping can only be updated by theverifyBundle()function(more on this in the following section)when the bundle is submitted, the block builder needs to call theverifyBundle()function, and this function can only be called by a whitelisted block builders in awhitelistmappingverifyBundle(Txs txs, address sender, uint blockNumber) onlyBlockBuilder returns(bundleIndex)Note:onlyBlockBuilderis a modifier checking whether the caller is in thewhitelistthe purpose of this function is to make sure that the value in thebundleSize,bundleHash, inputted by the bundle sender is correct because bundle sender could game the system by randomly putting the arbitrarybundleSizeandbundleHashThe block builder will need to supply the three argumentstxs,sender,blockNumber. All three of these values can be found the in the bundle submitted by the bundle senderin the function, it’ll do the following:get the length oftxs, and usesenderandblockNumberto query thebundleSizeMap. If match, continue; otherwise revertcalculate thebundleHashfrom thetxs, and usesenderandblockNumberto query thebundleHashMap. if match, continue; otherwise revertUpdate thebundleVerificationMapto true by providing thesenderandblockNumberreturns thebundleIndexMapto get the index a bundle sender wantsIf the bundle index is accepted by the block builder, the block builder will add the bundle, with the tx callingverifyBundle()on the top of the bundle, to the block. Otherwise, the block builder discard the bundle.Note:verifyBundle()can be called in a simulation to get thebundleIndexMap💡 Technically, a block builder can still temper the bundle after this step. A malicious block builder could call theverifyBundle()function first, then to change the ordering, or insert its own transaction in the middle of the bundle, and include the tempered bundle into the block. All of the previous steps are for a block builder to verify the arguments supplied by the bundle sender instamp()function is correct.This is why we need the following stepsAfter the block has been built and mined on-chain. A third party could challenge by submitting a proof of integrity using Axiom V2In a challenge, a user could use a front-end application to query two values leveraging Axiom V2bundle hash:The front end app would require a user to provide two values:senderAddressandblockNumberthe address is the bundle sender’s address, andblockNumberis the block when bundle was submittedthe front-end app will call the Axiom V2 API to query the block info. In the payload, the front-end app would find the bundle submitted by thesenderby indexing thesenderAddress.Then, the front-end app would query thebundleSizeMapandbundleHashMapto get thebundleSizeandbundleHash, given thesenderAddressandblockNumberBy using the index number on a block, plus the bundle size, the front-end app could generate abundleHashvalue in the browser. If it matches thebundleHashresult from the Axiom V2 API, then it mean bundle was not tempered with; otherwise, it means the bundle has been temperedsubmit the proof to a staking contract where block builder’s stake will be slashedBundle index:using similar method, the front-end app could get thebundleIndexto see if the block builder actually put the bundle at the slot number a bundle sender requested in thestamp()function
Solution
It's made using Axiom's V2 to trustlessly prove the behavior of a block builder. Front-end is generated using Axiom V2's repl export feature. Additionally, a mock block builder to test the mechanism has also been provided.The mechanism works mostly by allowing sender and blockbuilder respectively insert transactions at the bottom and top of a bundle
Hackathon
ETHGlobal New York
2024
Prizes
- 🏆
🚀 Flashbots — Best Innovation
- 🏆
💡 Axiom — Most Creative