Files
NakasarandGitHub 84d67bfb57 Version 2.2.0
* ✨(Identity) Add isClaimValid for self-attested claims

* 🔖(2.2.0) Update version and CHANGELOG

* 🔖(2.2.0) Update version and CHANGELOG

* ✨(factory) Add getter for authority
2023-10-17 11:40:28 +02:00

355 lines
18 KiB
TypeScript

import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from "chai";
import {ethers} from "hardhat";
import { deployIdentityFixture } from '../fixtures';
describe('Identity', () => {
describe('Claims', () => {
describe('addClaim', () => {
describe('when the claim is self-attested (issuer is identity address)', () => {
describe('when the claim is not valid', () => {
it('should add the claim anyway', async () => {
const { aliceIdentity, aliceWallet } = await loadFixture(deployIdentityFixture);
const claim = {
identity: aliceIdentity.address,
issuer: aliceIdentity.address,
topic: 42,
scheme: 1,
data: '0x0042',
signature: '',
uri: 'https://example.com',
};
claim.signature = await aliceWallet.signMessage(ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, '0x101010']))));
const tx = await aliceIdentity.connect(aliceWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
await expect(tx).to.emit(aliceIdentity, 'ClaimAdded').withArgs(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic])), claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
await expect(aliceIdentity.isClaimValid(claim.identity, claim.topic, claim.signature, claim.data)).to.eventually.equal(false);
});
});
describe('when the claim is valid', () => {
let claim = { identity: '', issuer: '', topic: 0, scheme: 1, data: '', uri: '', signature: '' };
before(async () => {
const { aliceIdentity, aliceWallet } = await loadFixture(deployIdentityFixture);
claim = {
identity: aliceIdentity.address,
issuer: aliceIdentity.address,
topic: 42,
scheme: 1,
data: '0x0042',
signature: '',
uri: 'https://example.com',
};
claim.signature = await aliceWallet.signMessage(ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, claim.data]))));
});
describe('when caller is the identity itself (execute)', () => {
it('should add the claim', async () => {
const { aliceIdentity, aliceWallet, bobWallet } = await loadFixture(deployIdentityFixture);
const action = {
to: aliceIdentity.address,
value: 0,
data: new ethers.utils.Interface([
'function addClaim(uint256 topic, uint256 scheme, address issuer, bytes calldata signature, bytes calldata data, string calldata uri) external returns (bytes32 claimRequestId)'
]).encodeFunctionData('addClaim', [
claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri
]),
};
await aliceIdentity.connect(bobWallet).execute(action.to, action.value, action.data);
const tx = await aliceIdentity.connect(aliceWallet).approve(0, true);
await expect(tx).to.emit(aliceIdentity, 'ClaimAdded').withArgs(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic])), claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
await expect(tx).to.emit(aliceIdentity, 'Approved');
await expect(tx).to.emit(aliceIdentity, 'Executed');
await expect(aliceIdentity.isClaimValid(claim.identity, claim.topic, claim.signature, claim.data)).to.eventually.equal(true);
});
});
describe('when caller is a CLAIM or MANAGEMENT key', () => {
it('should add the claim', async () => {
it('should add the claim anyway', async () => {
const { aliceIdentity, aliceWallet } = await loadFixture(deployIdentityFixture);
const tx = await aliceIdentity.connect(aliceWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
await expect(tx).to.emit(aliceIdentity, 'ClaimAdded').withArgs(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic])), claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
});
});
});
describe('when caller is not a CLAIM key', () => {
it('should revert for missing permission', async () => {
const { aliceIdentity, bobWallet } = await loadFixture(deployIdentityFixture);
await expect(aliceIdentity.connect(bobWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri)).to.be.revertedWith('Permissions: Sender does not have claim signer key');
});
});
});
});
describe('when the claim is from a claim issuer', () => {
describe('when the claim is not valid', () => {
it('should revert for invalid claim', async () => {
const { aliceIdentity, aliceWallet, claimIssuerWallet, claimIssuer } = await loadFixture(deployIdentityFixture);
const claim = {
identity: aliceIdentity.address,
issuer: claimIssuer.address,
topic: 42,
scheme: 1,
data: '0x0042',
signature: '',
uri: 'https://example.com',
};
claim.signature = await claimIssuerWallet.signMessage(ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, '0x10101010']))));
await expect(aliceIdentity.connect(aliceWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri)).to.be.revertedWith('invalid claim');
});
});
describe('when the claim is valid', () => {
let claim = { identity: '', issuer: '', topic: 0, scheme: 1, data: '', uri: '', signature: '' };
before(async () => {
const { aliceIdentity, claimIssuer, claimIssuerWallet } = await loadFixture(deployIdentityFixture);
claim = {
identity: aliceIdentity.address,
issuer: claimIssuer.address,
topic: 42,
scheme: 1,
data: '0x0042',
signature: '',
uri: 'https://example.com',
};
claim.signature = await claimIssuerWallet.signMessage(ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, claim.data]))));
});
describe('when caller is the identity itself (execute)', () => {
it('should add the claim', async () => {
const { aliceIdentity, aliceWallet, bobWallet } = await loadFixture(deployIdentityFixture);
const action = {
to: aliceIdentity.address,
value: 0,
data: new ethers.utils.Interface([
'function addClaim(uint256 topic, uint256 scheme, address issuer, bytes calldata signature, bytes calldata data, string calldata uri) external returns (bytes32 claimRequestId)'
]).encodeFunctionData('addClaim', [
claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri
]),
};
await aliceIdentity.connect(bobWallet).execute(action.to, action.value, action.data);
const tx = await aliceIdentity.connect(aliceWallet).approve(0, true);
await expect(tx).to.emit(aliceIdentity, 'ClaimAdded').withArgs(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic])), claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
await expect(tx).to.emit(aliceIdentity, 'Approved');
await expect(tx).to.emit(aliceIdentity, 'Executed');
});
});
describe('when caller is a CLAIM or MANAGEMENT key', () => {
it('should add the claim', async () => {
it('should add the claim anyway', async () => {
const { aliceIdentity, aliceWallet } = await loadFixture(deployIdentityFixture);
const tx = await aliceIdentity.connect(aliceWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
await expect(tx).to.emit(aliceIdentity, 'ClaimAdded').withArgs(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic])), claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
});
});
});
describe('when caller is not a CLAIM key', () => {
it('should revert for missing permission', async () => {
const { aliceIdentity, bobWallet } = await loadFixture(deployIdentityFixture);
await expect(aliceIdentity.connect(bobWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri)).to.be.revertedWith('Permissions: Sender does not have claim signer key');
});
});
});
});
});
describe('updateClaim (addClaim)', () => {
describe('when there is already a claim from this issuer and this topic', () => {
let aliceIdentity: ethers.Contract;
let aliceWallet: ethers.Wallet;
let claimIssuer: ethers.Contract;
let claimIssuerWallet: ethers.Wallet;
before(async () => {
const params = await loadFixture(deployIdentityFixture);
aliceIdentity = params.aliceIdentity;
aliceWallet = params.aliceWallet;
claimIssuer = params.claimIssuer;
claimIssuerWallet = params.claimIssuerWallet;
const claim = {
identity: aliceIdentity.address,
issuer: claimIssuer.address,
topic: 42,
scheme: 1,
data: '0x0042',
signature: '',
uri: 'https://example.com',
};
claim.signature = await claimIssuerWallet.signMessage(ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, claim.data]))));
await aliceIdentity.connect(aliceWallet).addClaim(
claim.topic,
claim.scheme,
claim.issuer,
claim.signature,
claim.data,
claim.uri,
);
});
it('should replace the existing claim', async () => {
const claim = {
identity: aliceIdentity.address,
issuer: claimIssuer.address,
topic: 42,
scheme: 1,
data: '0x004200101010',
signature: '',
uri: 'https://example.com',
};
claim.signature = await claimIssuerWallet.signMessage(ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, claim.data]))));
const tx = await aliceIdentity.connect(aliceWallet).addClaim(
claim.topic,
claim.scheme,
claim.issuer,
claim.signature,
claim.data,
claim.uri,
);
await expect(tx).to.emit(aliceIdentity, 'ClaimChanged').withArgs(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic])), claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
});
});
});
describe('removeClaim', () => {
describe('When caller is the identity itself (execute)', () => {
it('should remove an existing claim', async () => {
const { aliceIdentity, aliceWallet, bobWallet, claimIssuer, claimIssuerWallet } = await loadFixture(deployIdentityFixture);
const claim = {
identity: aliceIdentity.address,
issuer: claimIssuer.address,
topic: 42,
scheme: 1,
data: '0x0042',
signature: '',
uri: 'https://example.com',
};
const claimId = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic]));
claim.signature = await claimIssuerWallet.signMessage(ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, claim.data]))));
await aliceIdentity.connect(aliceWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
const action = {
to: aliceIdentity.address,
value: 0,
data: new ethers.utils.Interface([
'function removeClaim(bytes32 claimId) external returns (bool success)'
]).encodeFunctionData('removeClaim', [
claimId,
]),
};
await aliceIdentity.connect(bobWallet).execute(action.to, action.value, action.data);
const tx = await aliceIdentity.connect(aliceWallet).approve(0, true);
await expect(tx).to.emit(aliceIdentity, 'ClaimRemoved').withArgs(claimId, claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
});
});
describe('When caller is not a CLAIM key', () => {
it('should revert for missing permission', async () => {
const { aliceIdentity, bobWallet, claimIssuer } = await loadFixture(deployIdentityFixture);
const claimId = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claimIssuer.address, 42]));
await expect(aliceIdentity.connect(bobWallet).removeClaim(claimId)).to.be.revertedWith('Permissions: Sender does not have claim signer key');
});
});
describe('When claim does not exist', () => {
it('should revert for non existing claim', async () => {
const { aliceIdentity, carolWallet, claimIssuer } = await loadFixture(deployIdentityFixture);
const claimId = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claimIssuer.address, 42]));
await expect(aliceIdentity.connect(carolWallet).removeClaim(claimId)).to.be.revertedWith('NonExisting: There is no claim with this ID');
});
});
describe('When claim does exist', () => {
it('should remove the claim', async () => {
const { aliceIdentity, aliceWallet, claimIssuer, claimIssuerWallet } = await loadFixture(deployIdentityFixture);
const claim = {
identity: aliceIdentity.address,
issuer: claimIssuer.address,
topic: 42,
scheme: 1,
data: '0x0042',
signature: '',
uri: 'https://example.com',
};
const claimId = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claim.issuer, claim.topic]));
claim.signature = await claimIssuerWallet.signMessage(ethers.utils.arrayify(ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes'], [claim.identity, claim.topic, claim.data]))));
await aliceIdentity.connect(aliceWallet).addClaim(claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
const tx = await aliceIdentity.connect(aliceWallet).removeClaim(claimId);
await expect(tx).to.emit(aliceIdentity, 'ClaimRemoved').withArgs(claimId, claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
});
});
});
describe('getClaim', () => {
describe('when claim does not exist', () => {
it('should return an empty struct', async () => {
const { aliceIdentity, claimIssuer } = await loadFixture(deployIdentityFixture);
const claimId = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [claimIssuer.address, 42]));
const found = await aliceIdentity.getClaim(claimId);
expect(found.issuer).to.equal(ethers.constants.AddressZero);
expect(found.topic).to.equal(0);
expect(found.scheme).to.equal(0);
expect(found.data).to.equal('0x');
expect(found.signature).to.equal('0x');
expect(found.uri).to.equal('');
});
});
describe('when claim does exist', () => {
it('should return the claim', async () => {
const { aliceIdentity, aliceClaim666 } = await loadFixture(deployIdentityFixture);
const found = await aliceIdentity.getClaim(aliceClaim666.id);
expect(found.issuer).to.equal(aliceClaim666.issuer);
expect(found.topic).to.equal(aliceClaim666.topic);
expect(found.scheme).to.equal(aliceClaim666.scheme);
expect(found.data).to.equal(aliceClaim666.data);
expect(found.signature).to.equal(aliceClaim666.signature);
expect(found.uri).to.equal(aliceClaim666.uri);
});
});
});
describe('getClaimIdsByTopic', () => {
it('should return an empty array when there are no claims for the topic', async () => {
const { aliceIdentity } = await loadFixture(deployIdentityFixture);
await expect(aliceIdentity.getClaimIdsByTopic(101010)).to.eventually.deep.equal([]);
});
it('should return an array of claim Id existing fo the topic', async () => {
const { aliceIdentity, aliceClaim666 } = await loadFixture(deployIdentityFixture);
await expect(aliceIdentity.getClaimIdsByTopic(aliceClaim666.topic)).to.eventually.deep.equal([aliceClaim666.id]);
});
});
});
});