jest . setTimeout ( 1200000 ) ; import { executeCircuit , getSignalByName , compileAndLoadCircuit } from '../' ; import { config } from 'maci-config' ; import { MaciState } from 'maci-core' ; import { Keypair , StateLeaf , Command , Message , } from 'maci-domainobjs' ; import { genRandomSalt , } from 'maci-crypto' ; const batchSize = config . maci . messageBatchSize ; const stateTreeDepth = config . maci . merkleTrees . stateTreeDepth ; const messageTreeDepth = config . maci . merkleTrees . messageTreeDepth ; const voteOptionTreeDepth = config . maci . merkleTrees . voteOptionTreeDepth ; const voteOptionsMaxIndex = config . maci . voteOptionsMaxLeafIndex ; const initialVoiceCreditBalance = config . maci . initialVoiceCreditBalance ; const user = new Keypair ( ) ; const coordinator = new Keypair ( ) ; describe ( 'State tree root update verification circuit' , ( ) => { let circuit ; const voteWeight = BigInt ( 2 ) ; beforeAll ( async ( ) => { circuit = await compileAndLoadCircuit ( config . env === 'test' ? 'test/batchUpdateStateTree_test.circom' : 'prod/batchUpdateStateTree_small.circom' ) ; } ) ; it ( 'BatchUpdateStateTree should produce the correct state root from a partially filled batch' , async ( ) => { const maciState = new MaciState ( coordinator , stateTreeDepth , messageTreeDepth , voteOptionTreeDepth , voteOptionsMaxIndex ) ; maciState . signUp ( user . pubKey , initialVoiceCreditBalance ) ; const stateRootBefore = maciState . genStateRoot ( ) ; const command = new Command ( BigInt ( 1 ) , user . pubKey , BigInt ( 0 ) , voteWeight , BigInt ( 1 ) , genRandomSalt ( ) ) ; const signature = command . sign ( user . privKey ) ; const sharedKey = Keypair . genEcdhSharedKey ( user . privKey , coordinator . pubKey ) ; const message = command . encrypt ( signature , sharedKey ) ; maciState . publishMessage ( message , user . pubKey ) ; const randomStateLeaf = StateLeaf . genRandomLeaf ( ) ; const circuitInputs = maciState . genBatchUpdateStateTreeCircuitInputs ( 0 , batchSize , randomStateLeaf ) ; const witness = await executeCircuit ( circuit , circuitInputs ) ; const circuitNewStateRoot = getSignalByName ( circuit , witness , 'main.root' ) . toString ( ) ; maciState . batchProcessMessage ( 0 , batchSize , randomStateLeaf ) ; const stateRootAfter = maciState . genStateRoot ( ) ; expect ( stateRootBefore . toString ( ) ) . not . toEqual ( stateRootAfter ) ; expect ( circuitNewStateRoot . toString ( ) ) . toEqual ( stateRootAfter . toString ( ) ) ; } ) ; it ( 'BatchUpdateStateTree should produce the correct state root from a full batch' , async ( ) => { const randomStateLeaf = StateLeaf . genRandomLeaf ( ) ; const maciState = new MaciState ( coordinator , stateTreeDepth , messageTreeDepth , voteOptionTreeDepth , voteOptionsMaxIndex ) ; maciState . signUp ( user . pubKey , initialVoiceCreditBalance ) ; const stateRootBefore = maciState . genStateRoot ( ) ; const messages = [ ] ; for ( let i = 0 ; i < batchSize - 1 ; i ++ ) { const voteWeight = BigInt ( i + 1 ) ; const command = new Command ( BigInt ( 1 ) , user . pubKey , BigInt ( 0 ) , voteWeight , BigInt ( i + 1 ) , genRandomSalt ( ) ) ; const signature = command . sign ( user . privKey ) ; const sharedKey = Keypair . genEcdhSharedKey ( user . privKey , coordinator . pubKey ) ; const message = command . encrypt ( signature , sharedKey ) ; messages . push ( message ) ; maciState . publishMessage ( message , user . pubKey ) ; } const copiedState = maciState . copy ( ) ; copiedState . batchProcessMessage ( 0 , batchSize , randomStateLeaf ) ; expect ( copiedState . users [ 0 ] . voiceCreditBalance . toString ( ) ) . toEqual ( ( initialVoiceCreditBalance - 1 ) . toString ( ) ) ; console . log ( copiedState . genStateRoot ( ) ) ; const circuitInputs = maciState . genBatchUpdateStateTreeCircuitInputs ( 0 , batchSize , randomStateLeaf ) ; const witness = await executeCircuit ( circuit , circuitInputs ) ; const circuitNewStateRoot = getSignalByName ( circuit , witness , 'main.root' ) . toString ( ) ; maciState . batchProcessMessage ( 0 , batchSize , randomStateLeaf ) ; const stateRootAfter = maciState . genStateRoot ( ) ; expect ( stateRootBefore . toString ( ) ) . not . toEqual ( stateRootAfter ) ; expect ( circuitNewStateRoot . toString ( ) ) . toEqual ( stateRootAfter . toString ( ) ) ; } ) ; it ( 'BatchUpdateStateTree should produce the correct state root from one and a half batches' , async ( ) => { const randomStateLeaf = StateLeaf . genRandomLeaf ( ) ; const maciState = new MaciState ( coordinator , stateTreeDepth , messageTreeDepth , voteOptionTreeDepth , voteOptionsMaxIndex ) ; maciState . signUp ( user . pubKey , initialVoiceCreditBalance ) ; const stateRootBefore = maciState . genStateRoot ( ) ; const messages = [ ] ; for ( let i = 0 ; i < batchSize + Math . floor ( batchSize / 2 ) ; i ++ ) { const voteWeight = BigInt ( i + 1 ) ; const command = new Command ( BigInt ( 1 ) , user . pubKey , BigInt ( 0 ) , voteWeight , BigInt ( i + 1 ) , genRandomSalt ( ) ) ; const signature = command . sign ( user . privKey ) ; const sharedKey = Keypair . genEcdhSharedKey ( user . privKey , coordinator . pubKey ) ; const message = command . encrypt ( signature , sharedKey ) ; messages . push ( message ) ; maciState . publishMessage ( message , user . pubKey ) ; } let numAssertions = 0 ; const x = Math . floor ( maciState . messages . length / batchSize ) ; for ( let i = x * batchSize ; i >= 0 ; i -= batchSize ) { const circuitInputs = maciState . genBatchUpdateStateTreeCircuitInputs ( i , batchSize , randomStateLeaf ) ; const witness = await executeCircuit ( circuit , circuitInputs ) ; const circuitNewStateRoot = getSignalByName ( circuit , witness , 'main.root' ) . toString ( ) ; maciState . batchProcessMessage ( i , batchSize , randomStateLeaf ) ; const stateRootAfter = maciState . genStateRoot ( ) ; expect ( stateRootBefore . toString ( ) ) . not . toEqual ( stateRootAfter ) ; expect ( circuitNewStateRoot . toString ( ) ) . toEqual ( stateRootAfter . toString ( ) ) ; numAssertions += 2 ; } expect . assertions ( numAssertions ) ; } ) ; } ) ;