diff --git a/yarn-project/archiver/src/store/contract_class_store.test.ts b/yarn-project/archiver/src/store/contract_class_store.test.ts index 42a389c84031..dd90be2e91a4 100644 --- a/yarn-project/archiver/src/store/contract_class_store.test.ts +++ b/yarn-project/archiver/src/store/contract_class_store.test.ts @@ -43,10 +43,13 @@ describe('ContractClassStore', () => { await expect(contractClassStore.getContractClass(contractClass.id)).resolves.toBeUndefined(); }); - it('throws if the same contract class is added again', async () => { + it('is a no-op if the same contract class is added again at a later block', async () => { await expect( contractClassStore.addContractClasses([await withCommitment(contractClass)], BlockNumber(blockNum + 1)), - ).rejects.toThrow(/already exists/); + ).resolves.toBe(true); + // Original l2BlockNumber is preserved, so a later delete at blockNum + 1 should not remove it. + await contractClassStore.deleteContractClasses([contractClass], BlockNumber(blockNum + 1)); + await expect(contractClassStore.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass); }); it('returns contract class if deleted at a later block number', async () => { diff --git a/yarn-project/archiver/src/store/contract_class_store.ts b/yarn-project/archiver/src/store/contract_class_store.ts index ea3b5a48ef56..6e0c98996c04 100644 --- a/yarn-project/archiver/src/store/contract_class_store.ts +++ b/yarn-project/archiver/src/store/contract_class_store.ts @@ -49,14 +49,14 @@ export class ContractClassStore { ): Promise { await this.db.transactionAsync(async () => { const key = contractClass.id.toString(); - if (await this.#contractClasses.hasAsync(key)) { - throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`); - } - await this.#contractClasses.set( + // Class id is content-derived (artifactHash, privateFunctionsRoot, publicBytecodeCommitment), + // so a duplicate add carries identical data. Keep the original entry's l2BlockNumber so that + // a later rollback at a higher block leaves the class registered at its earliest block. + await this.#contractClasses.setIfNotExists( key, serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }), ); - await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer()); + await this.#bytecodeCommitments.setIfNotExists(key, bytecodeCommitment.toBuffer()); }); } diff --git a/yarn-project/archiver/src/store/contract_instance_store.test.ts b/yarn-project/archiver/src/store/contract_instance_store.test.ts index f6330c2ed0a2..a58a866813f4 100644 --- a/yarn-project/archiver/src/store/contract_instance_store.test.ts +++ b/yarn-project/archiver/src/store/contract_instance_store.test.ts @@ -48,10 +48,11 @@ describe('ContractInstanceStore', () => { ).resolves.toBeUndefined(); }); - it('throws when adding the same contract instance twice', async () => { - await expect(contractInstanceStore.addContractInstances([contractInstance], BlockNumber(2))).rejects.toThrow( - /already exists/, - ); + it('is a no-op when adding the same contract instance twice', async () => { + await expect(contractInstanceStore.addContractInstances([contractInstance], BlockNumber(2))).resolves.toBe(true); + await expect( + contractInstanceStore.getContractInstance(contractInstance.address, timestamp), + ).resolves.toMatchObject(contractInstance); }); }); diff --git a/yarn-project/archiver/src/store/contract_instance_store.ts b/yarn-project/archiver/src/store/contract_instance_store.ts index eed2aa55b681..7cd62fbb4749 100644 --- a/yarn-project/archiver/src/store/contract_instance_store.ts +++ b/yarn-project/archiver/src/store/contract_instance_store.ts @@ -71,13 +71,16 @@ export class ContractInstanceStore { addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise { return this.db.transactionAsync(async () => { const key = contractInstance.address.toString(); - if (await this.#contractInstances.hasAsync(key)) { - throw new Error( - `Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`, - ); + // Instance address is content-derived from instance fields, so a duplicate add carries + // identical data. Keep the original entry's blockNumber so that a later rollback at a higher + // block leaves the instance registered at its earliest block. + const inserted = await this.#contractInstances.setIfNotExists( + key, + new SerializableContractInstance(contractInstance).toBuffer(), + ); + if (inserted) { + await this.#contractInstancePublishedAt.set(key, blockNumber); } - await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer()); - await this.#contractInstancePublishedAt.set(key, blockNumber); }); }