Skip to content

Commit

Permalink
Distributors now use own ids for distributions that are keccak(distri…
Browse files Browse the repository at this point in the history
…butionId,initializerId)
  • Loading branch information
peersky committed Sep 11, 2024
1 parent a4196a7 commit 1424330
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 40 deletions.
50 changes: 30 additions & 20 deletions src/abstracts/Distributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import "../interfaces/IDistributor.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "../interfaces/IInitializer.sol";
import "../abstracts/CodeIndexer.sol";

abstract contract Distributor is IDistributor, CodeIndexer {

struct DistributionComponent {
bytes32 id;
address initializer;
}
using EnumerableSet for EnumerableSet.Bytes32Set;
EnumerableSet.Bytes32Set private distirbutionsSet;
mapping(bytes32 => IInitializer) private initializers;
mapping(address => bytes32) private distributionOf;
mapping(bytes32 => DistributionComponent) private distributionComponents;

function getDistributions() public view returns (bytes32[] memory) {
return distirbutionsSet.values();
Expand All @@ -22,46 +27,50 @@ abstract contract Distributor is IDistributor, CodeIndexer {
return distributionOf[instance];
}

function getDistributionURI(bytes32 id) public view returns (string memory) {
function getDistributionURI(bytes32 distributorsId) public view returns (string memory) {
DistributionComponent memory distributionComponent = distributionComponents[distributorsId];
ICodeIndex codeIndex = getContractsIndex();
return IDistribution(codeIndex.get(id)).getMetadata();
return IDistribution(codeIndex.get(distributionComponent.id)).getMetadata();
}

function _addDistribution(bytes32 id, bytes32 initId) internal virtual {
ICodeIndex codeIndex = getContractsIndex();
address initializerAddress = codeIndex.get(initId);
if (codeIndex.get(id) == address(0)) revert DistributionNotFound(id);
if (initializerAddress == address(0) && initId != bytes32(0)) revert InitializerNotFound(initId);
if (distirbutionsSet.contains(id)) revert DistributionExists(id);
distirbutionsSet.add(id);
initializers[id] = IInitializer(initializerAddress);
bytes32 distributorsId = keccak256(abi.encode(id,initId));
if (distirbutionsSet.contains(distributorsId)) revert DistributionExists(distributorsId);
distirbutionsSet.add(distributorsId);
distributionComponents[distributorsId] = DistributionComponent(id, initializerAddress);
emit DistributionAdded(id, initId);
}

function _removeDistribution(bytes32 id) internal virtual {
if (!distirbutionsSet.contains(id)) revert DistributionNotFound(id);
distirbutionsSet.remove(id);
emit DistributionRemoved(id);
function _removeDistribution(bytes32 distributorsId) internal virtual {
if (!distirbutionsSet.contains(distributorsId)) revert DistributionNotFound(distributorsId);
distirbutionsSet.remove(distributorsId);
initializers[distributorsId] = IInitializer(address(0));
emit DistributionRemoved(distributorsId);
}

function _instantiate(bytes32 id, bytes calldata args) internal virtual returns (address[] memory instances, bytes32 distributionName, uint256 distributionVersion) {
function _instantiate(bytes32 distributorsId, bytes calldata args) internal virtual returns (address[] memory instances, bytes32 distributionName, uint256 distributionVersion) {
ICodeIndex codeIndex = getContractsIndex();
if (!distirbutionsSet.contains(id)) revert DistributionNotFound(id);
(instances, distributionName, distributionVersion) = IDistribution(codeIndex.get(id)).instantiate();
if (!distirbutionsSet.contains(distributorsId)) revert DistributionNotFound(distributorsId);
DistributionComponent memory distributionComponent = distributionComponents[distributorsId];
(instances, distributionName, distributionVersion) = IDistribution(codeIndex.get(distributionComponent.id)).instantiate();
bytes4 selector = IInitializer.initialize.selector;
// This ensures instance owner (distributor) performs initialization.
// It is distirbutor responsibility to make sure calldata and initializer are safe to execute
address initializer = address(initializers[id]);
address initializer = address(initializers[distributionComponent.id]);
if (initializer != address(0)) {
(bool success, bytes memory result) = address(initializers[id]).delegatecall(
(bool success, bytes memory result) = address(distributionComponent.initializer).delegatecall(
abi.encodeWithSelector(selector, instances, args)
);
require(success, string(result));
}
for (uint256 i = 0; i < instances.length; i++) {
distributionOf[instances[i]] = id;
distributionOf[instances[i]] = distributorsId;
}
emit Instantiated(id, args);
emit Instantiated(distributorsId, args);
return (instances, distributionName, distributionVersion);
}

Expand All @@ -72,9 +81,10 @@ abstract contract Distributor is IDistributor, CodeIndexer {
uint256,
bytes memory
) public view virtual returns (bytes memory) {
bytes32 id = distributionOf[instance];
if (id != bytes32(0) && distirbutionsSet.contains(id) == true) {
return abi.encode(id, "");
bytes32 distributorsId = distributionOf[instance];
// DistributionComponent memory distributionComponent = distributionComponents[distributorsId];
if (distributorsId != bytes32(0) && distirbutionsSet.contains(distributorsId) == true) {
return abi.encode(distributorsId, "");
} else {
revert InvalidInstance(instance);
}
Expand Down
10 changes: 5 additions & 5 deletions src/interfaces/IDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ interface IDistributor is ILayer {

event DistributionAdded(bytes32 indexed id, bytes32 indexed initializerId);

function getDistributions() external view returns (bytes32[] memory ids);
function getDistributions() external view returns (bytes32[] memory distributorIds);

function getDistributionURI(bytes32 id) external view returns (string memory);
function getDistributionURI(bytes32 distributorId) external view returns (string memory);

function instantiate(bytes32 id, bytes calldata args) external returns (address[] memory, bytes32 distributionName, uint256 distributionVersion);
function instantiate(bytes32 distributorId, bytes calldata args) external returns (address[] memory, bytes32 distributionName, uint256 distributionVersion);

function addDistribution(bytes32 id, bytes32 initializer) external;
function addDistribution(bytes32 distributorId, bytes32 initializer) external;

function removeDistribution(bytes32 id) external;
function removeDistribution(bytes32 distributorId) external;
}
13 changes: 9 additions & 4 deletions test/eds/Distributor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe("Distributor", function () {
let distributor: OwnableDistributor;
let deployer: SignerWithAddress;
let owner: SignerWithAddress;
let distributorsId: any;
let cloneDistributionId: any;

beforeEach(async function () {
Expand All @@ -40,6 +41,10 @@ describe("Distributor", function () {
await cloneDistribution.deployed();
const code = await cloneDistribution.provider.getCode(cloneDistribution.address);
cloneDistributionId = ethers.utils.keccak256(code);
distributorsId = ethers.utils.solidityKeccak256(
["bytes32", "bytes32"],
[cloneDistributionId, ethers.utils.formatBytes32String("")]
);
await codeIndex.register(cloneDistribution.address);
});

Expand Down Expand Up @@ -83,12 +88,12 @@ describe("Distributor", function () {
expect(
await distributor
.connect(owner)
.instantiate(cloneDistributionId, ethers.utils.formatBytes32String(""))
.instantiate(distributorsId, ethers.utils.formatBytes32String(""))
).to.emit(distributor, "Instantiated");
});

it("Is possible to remove a distribution", async function () {
expect(await distributor.connect(owner).removeDistribution(cloneDistributionId)).to.emit(
expect(await distributor.connect(owner).removeDistribution(distributorsId)).to.emit(
distributor,
"DistributionRemoved"
);
Expand All @@ -100,12 +105,12 @@ describe("Distributor", function () {
let receipt = await (
await distributor
.connect(owner)
.instantiate(cloneDistributionId, ethers.utils.formatBytes32String(""))
.instantiate(distributorsId, ethers.utils.formatBytes32String(""))
).wait();
let parsed = utils.getSuperInterface().parseLog(receipt.logs[0]);
instanceAddress = parsed.args.instances[0];
console.log(instanceAddress);
await distributor.connect(owner).removeDistribution(cloneDistributionId);
await distributor.connect(owner).removeDistribution(distributorsId);
});
it("Instance is invalid upon check", async () => {
await expect(
Expand Down
27 changes: 16 additions & 11 deletions test/eds/Installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe("Installer", function () {
let owner: SignerWithAddress;
let target: SignerWithAddress;
let cloneDistributionId: any;
let distributorsId: any;
let installer: MockInstaller;

beforeEach(async function () {
Expand Down Expand Up @@ -47,6 +48,10 @@ describe("Installer", function () {
distributor
.connect(owner)
.addDistribution(cloneDistributionId, ethers.utils.formatBytes32String(""));
distributorsId = ethers.utils.solidityKeccak256(
["bytes32", "bytes32"],
[cloneDistributionId, ethers.utils.formatBytes32String("")]
);
const Installer = (await ethers.getContractFactory("MockInstaller")) as MockInstaller__factory;
installer = await Installer.deploy(target.address, owner.address);
});
Expand Down Expand Up @@ -78,7 +83,7 @@ describe("Installer", function () {
await installer.connect(owner).whitelistDistributor(distributor.address);

expect(
await installer.connect(deployer).install(distributor.address, cloneDistributionId, "0x")
await installer.connect(deployer).install(distributor.address, distributorsId, "0x")
).to.emit(installer, "Installed");
});
it("can List distributors", async function () {
Expand All @@ -89,7 +94,7 @@ describe("Installer", function () {
});
it("Can get instances by id", async function () {
await installer.connect(owner).whitelistDistributor(distributor.address);
await installer.connect(owner).install(distributor.address, cloneDistributionId, "0x");
await installer.connect(owner).install(distributor.address, distributorsId, "0x");
const instanceNum = await installer.connect(owner).getInstancesNum();
expect((await installer.connect(owner).getInstance(instanceNum)).length).to.be.eq(1);
let instanceAddress = (await installer.connect(owner).getInstance(instanceNum))[0];
Expand All @@ -103,7 +108,7 @@ describe("Installer", function () {

it("Allows whitelisted distributor only valid instances to call target", async () => {
await installer.connect(owner).whitelistDistributor(distributor.address);
await installer.connect(owner).install(distributor.address, cloneDistributionId, "0x");
await installer.connect(owner).install(distributor.address, distributorsId, "0x");
const instanceNum = await installer.connect(owner).getInstancesNum();
let instanceAddress = (await installer.connect(target).getInstance(instanceNum))[0];
await expect(
Expand All @@ -114,15 +119,15 @@ describe("Installer", function () {
installer.connect(target).beforeCall("0x", "0x00000000", instanceAddress, "0", "0x")
).to.be.not.revertedWithCustomError(installer, "NotAnInstance");

await distributor.connect(owner).removeDistribution(cloneDistributionId);
await distributor.connect(owner).removeDistribution(distributorsId);
await expect(
installer.connect(target).beforeCall("0x", "0x00000000", instanceAddress, "0", "0x")
).to.be.revertedWithCustomError(distributor, "InvalidInstance");
});

it("Allows valid distributions added by distributor and distribution id to call target", async () => {
await installer.connect(owner).allowDistribution(distributor.address, cloneDistributionId);
await installer.connect(owner).install(distributor.address, cloneDistributionId, "0x");
await installer.connect(owner).allowDistribution(distributor.address, distributorsId);
await installer.connect(owner).install(distributor.address, distributorsId, "0x");
const instanceNum = await installer.connect(owner).getInstancesNum();
let instanceAddress = (await installer.connect(target).getInstance(instanceNum))[0];
await expect(
Expand All @@ -133,19 +138,19 @@ describe("Installer", function () {
installer.connect(target).beforeCall("0x", "0x00000000", instanceAddress, "0", "0x")
).to.be.not.revertedWithCustomError(installer, "NotAnInstance");

await distributor.connect(owner).removeDistribution(cloneDistributionId);
await distributor.connect(owner).removeDistribution(distributorsId);
await expect(
installer.connect(target).beforeCall("0x", "0x00000000", instanceAddress, "0", "0x")
).to.be.revertedWithCustomError(distributor, "InvalidInstance");
});

it("Reverts when valid distributions added by distributor and distribution id were removed", async () => {
await installer.connect(owner).allowDistribution(distributor.address, cloneDistributionId);
await installer.connect(owner).install(distributor.address, cloneDistributionId, "0x");
await installer.connect(owner).allowDistribution(distributor.address, distributorsId);
await installer.connect(owner).install(distributor.address, distributorsId, "0x");
const instanceNum = await installer.connect(owner).getInstancesNum();
let instanceAddress = (await installer.connect(target).getInstance(instanceNum))[0];

await installer.connect(owner).disallowDistribution(distributor.address, cloneDistributionId);
await installer.connect(owner).disallowDistribution(distributor.address, distributorsId);

await expect(
installer.connect(target).beforeCall("0x", "0x00000000", instanceAddress, "0", "0x")
Expand All @@ -154,7 +159,7 @@ describe("Installer", function () {

it("Does reverts on invalid target", async () => {
await installer.connect(owner).whitelistDistributor(distributor.address);
await installer.connect(owner).install(distributor.address, cloneDistributionId, "0x");
await installer.connect(owner).install(distributor.address, distributorsId, "0x");
const instanceNum = await installer.connect(owner).getInstancesNum();
let instanceAddress = (await installer.connect(target).getInstance(instanceNum))[0];
await expect(
Expand Down

0 comments on commit 1424330

Please sign in to comment.