--- tags: ZK,Bundler-Design --- # Bundler anti-replay attack scheme (researching) ## background In the account abstraction of 4337, in order to prevent replay attacks, a nonce variable is introduced into the AA wallet contract, which must be updated every time Tx is executed, thereby increasing the Gas overhead ## direction: - We need to meet our anti-replay attack requirements in a relatively Gas-saving way - Combining Tx's nonce + chainId two fields - The nonce in the tx of each account should increase monotonically by +1 - chainId is bound to the corresponding network - It can be solved by adding chainId to the off-chain signature parameter, and using block.chainId to calculate the hash comparison on the chain ## plan - The correctness of the nonce of the user Tx is guaranteed by ZKP - The EP contract does not need to maintain the nonce for each user separately, but maintains two StateHash and RecrusiveStateHash values ​​in the Verify contract (great gas savings) StateHash, RecrusiveStateHash iterative maintenance process ![](https://hackmd.io/_uploads/HJKflgEoi.png) ## Questions to be confirmed - Performance improvement of off-chain ZKP - Need to choose the appropriate merkel data structure - Process security audit (not found yet) - DA problem with user Nonce - The user's actual nonce is all stored off-chain, and nodes need to ensure data availability - When the EP contract executes Tx, it needs to log a nonce, which is convenient for users to build the state of the nonce by themselves – by Wen Bo ## Pseudocode description Prove process circom syntax description: ``` signal input UserAddress[N]; signal input CurUserNonce[N]; signal input CurRecrusiveStateHash; signal input PreRecrusiveStateHash; signal input CurStateHash; signal input NewUserNonce[N]; signal output NewRecrusiveStateHash; signal output NewStateHash; signal output PubCurStateHash; signal output PubCurRecrusiveStateHash; assert(MerkelHash(CurUserNonce[N]) == CurHash); assert(Hash(PreRecrusiveStateHash, CurHash) == CurRecrusiveStateHash); for(var i = 0; i < N;i ++) { assert(CurUserNonce[i] + 1 == NewUserNonce[i]); } NewStateHash <== MerkelHash(NewUserNonce[N]); NewRecrusiveStateHash <== Hash(CurRecrusiveStateHash, NewStateHash); PubCurStateHash <== MerkleHash(CurUserNonce[N]); PubCurRecrusiveStateHash <== CurRecrusiveStateHash; ``` Prove results: ``` ProofData; PublicInputs:{ NewStateHash, NewRecrusiveStateHash, PubCurStateHash, PubCurRecrusiveStateHash, } ``` The process of updating the status after Verify (EVM environment): ``` require(Verify(ProofData,PublicInputs)==True); require(self.CurStateHash == PublicInputs.PubCurStateHash); require(self.CurRecrusiveStateHash == PublicInputs.PubCurRecrusiveStateHash); self.CurStateHash = PublicInputs.NewStateHash; self.CurRecrusiveStateHash = PublicInputs.PubCurRecrusiveStateHash; ```