From de3b87bc0be9995b5b68bde57be2a61517648108 Mon Sep 17 00:00:00 2001 From: "David E. Perez Negron R." Date: Wed, 8 May 2024 19:15:25 -0600 Subject: [PATCH 1/3] Adding deployment scripts and README Update usage. --- README.md | 39 ++++++++++++++-- deploy.sh | 11 +++++ script/DeployedDappIndexer.s.sol | 65 ++++++++++++++++++++++++++ test/DappIndexer.t.sol.back | 80 -------------------------------- 4 files changed, 112 insertions(+), 83 deletions(-) create mode 100755 deploy.sh create mode 100644 script/DeployedDappIndexer.s.sol delete mode 100644 test/DappIndexer.t.sol.back diff --git a/README.md b/README.md index dc33141..fbe5b36 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ The goal is to use this as an alternative for ssl to web3 to verify its the corr - [x] Keep it as simple as posible. ## ToDo and wanted features: +- [ ] Integrate CIDStorage library to the Dapp Indexer smart contract - [ ] Create an event for metadata information about the CID. -- [ ] Support other type of hashes or structure storage - +- [ ] Support of the [CID Specification](https://github.com/multiformats/cid?tab=readme-ov-file) ## Usage @@ -41,11 +41,44 @@ $ forge snapshot $ anvil ``` +### Enviorment Variables + + ### Deploy +#### Option1: Deployment script (recommended) + ```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +$ ./deploy.sh ``` +> IMPORTANT: verify .env information before executing the deployment script + +**verify by attaching the deployed contract test:** + +Once you deployed the contract with the previous script execution you can test +the correct smartcontract operations by running the `DeployedDappIndexer.s.sol` +script + +should set after deploying the smart contract in the .env named after +DAPPINDEXERADDRS, once you update the address you can run the following +script to verify it was deployed correctly. + +```shell +$source .env +$forge script script/DeployedDappIndexer.s.sol:DappIndexerScript --fork-url $PROVIDER_URL --private-key $PK0 --broadcast +``` +#### Option2: Script deployment and test +using the script follows diferent verifications first before deploying the +contract, more about it here. + +```shell +$source .env +$forge script script/DappIndexer.s.sol:DappIndexerScript --fork-url $PROVIDER_URL --private-key $PK0 --broadcast +``` +> where: +> PROVIDER_URL can be either the anvil fork or a blockchain RPC +> $PK0 + ### Cast diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..cb4595c --- /dev/null +++ b/deploy.sh @@ -0,0 +1,11 @@ +#!/bin/sh +. ./.env +forge create --rpc-url $PROVIDER_URL \ + --constructor-args $ACC0 $ACC0 \ + --private-key $PK0 \ + src/DappIndexer.sol:DappIndexer + +#forge create --rpc-url $PROVIDER_URL \ +# --constructor-args $ACC0 $ACC0 \ +# --private-key $PK0 \ +# src/CIDStorage.sol:CIDStorage diff --git a/script/DeployedDappIndexer.s.sol b/script/DeployedDappIndexer.s.sol new file mode 100644 index 0000000..7599732 --- /dev/null +++ b/script/DeployedDappIndexer.s.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import {Script, console2} from "forge-std/Script.sol"; +import { DappIndexer} from "src/DappIndexer.sol"; +import { CIDStorage } from "src/CIDStorage.sol"; + +contract DappIndexerScript is Script { + address public dappIndexerAddr; + address public CIDStorageAddr; + DappIndexer public dappIdxr; + string[] public dappsURI; + address public owner; + + function setUp() public { + dappIndexerAddr = vm.envAddress("DAPPINDEXERADDRS"); + dappsURI = vm.envString("dapps", ' '); + // Deployed contract must have Owner as ACC0) + owner = vm.envAddress("ACC0"); + } + + // run is the entry point + function run() public { + uint256 deployerPrivateKey = vm.envUint("PK0"); + // startBroadcast and stopBraodcast will let us execute transactions anything between them + vm.startBroadcast(deployerPrivateKey); + // connect to the DappIndexer already deployed address + dappIdxr = DappIndexer(dappIndexerAddr); + + console2.log("The Dapps Indexer contract address is: ", address(dappIdxr)); + + // Define a Dapp name + bytes32 dapp_name = bytes32("Dapp01"); + + // Define DappURI splited into two bytes32 CIDs + bytes32 CID1; + bytes32 CID2; + + (CID1, CID2) = CIDStorage.stringToBytes32Pair(dappsURI[0]); + + // Add Dapp + console2.log("-----Adding Dapp-----"); + console2.log("Dapp Name is:"); + console2.logBytes32(dapp_name); + console2.log("Bytes32 first:"); + console2.logBytes32(CID1); + console2.log("Bytes32 second:"); + console2.logBytes32(CID2); + console2.log(CIDStorage.bytes32PairToString(CID1, CID2)); + dappIdxr.addDapp(dapp_name, DappIndexer.PackedCID(CID1, CID2)); + + // retrive Dapp + console2.log("-----retriving Dapp-----"); + DappIndexer.PackedCID memory dapp1 = dappIdxr.getDapp(dapp_name); + console2.log("Bytes32 first:"); + console2.logBytes32(dapp1.CID1); + console2.log("Bytes32 second:"); + console2.logBytes32(dapp1.CID2); + + // reconstruct the CID and verify + console2.log(CIDStorage.bytes32PairToString(dapp1.CID1, dapp1.CID2)); + + vm.stopBroadcast(); + } +} diff --git a/test/DappIndexer.t.sol.back b/test/DappIndexer.t.sol.back deleted file mode 100644 index 76d25d2..0000000 --- a/test/DappIndexer.t.sol.back +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.23; - -import { Test, console2 } from "forge-std/Test.sol"; -import { DappIndexer } from "src/DappIndexer.sol"; - -contract DappIndexerTest is Test { - DappIndexer public dappIdxr; - string[] public dappsURI; - - function setUp() public { - dappIdxr = new DappIndexer(address(this), address(this)); - dappsURI = vm.envString("dapps", ' '); - } - - function testAddDapp() public { - bytes32 CID1; - bytes32 CID2; - - //(CID1, CID2) = stringToBytes32Pair(dappsURI[0]); - string memory dapp= "bafkreibc6p3y36yjmeqqnttqfrpb2yttxa6aonoywxwdxl7nqym4jj3jwa"; - (CID1, CID2) = stringToBytes32Pair(dapp); - - // Add Dapp - console2.log("Bytes32 first:"); - console2.logBytes32(CID1); - console2.log("Bytes32 second:"); - console2.logBytes32(CID2); - console2.log(Bytes32PairToString(CID1, CID2)); - dappIdxr.addDapp(bytes32("Dapp01"), DappIndexer.PackedCID(CID1, CID2)); - // retrive Dapp - DappIndexer.PackedCID memory dapp1 = dappIdxr.getCID(bytes32("Dapp01")); - assertEq(CID1, dapp1.CID1); - assertEq(CID2, dapp1.CID2); - assertEq(dappsURI[0], Bytes32PairToString(dapp1.CID1, dapp1.CID2)); - } - - function Bytes32PairToString(bytes32 part1, bytes32 part2) public pure returns (string memory) { - // Concatenate the two bytes32 variables - bytes memory concatenatedBytes = abi.encodePacked(part1, part2); - - // Convert the concatenated bytes to a string - string memory str = string(concatenatedBytes); - - return str; - } - - function stringToBytes32Pair(string memory source) public pure returns (bytes32 part1, bytes32 part2) { - bytes memory sourceBytes = bytes(source); - require(sourceBytes.length == 59, "Source string must be 59 bytes"); - - assembly { - // Load the first 32 bytes of the string data - part1 := mload(add(sourceBytes, 32)) - // Load the next 32 bytes of the string data, then shift right by 3 bytes (24 bits) to remove unwanted bytes - part2 := mload(add(sourceBytes, 64)) - part2 := shl(24, part2) // Shift left to remove the last 5 bytes - part2 := shr(24, part2) // Shift right to align back to the least significant bits - } - } - //function stringToBytes32Pair(string memory source) public pure returns (bytes32 part1, bytes32 part2) { - // bytes memory sourceBytes = bytes(source); - // require(sourceBytes.length == 59, "URI string must equal to 59 bytes"); - - // assembly { - // // Load the first 32 bytes of the string data - // part1 := mload(add(sourceBytes, 32)) - // // Load the second 32 bytes of the string data - // part2 := mload(add(sourceBytes, 58)) - // } - //} -} -// function stringToBytes32Pair(string calldata str) external pure returns(bytes32 part1, bytes32 part2) { -// bytes memory sourceBytes = bytes(str); -// require(sourceBytes.length == 59, "Source string must be 59 bytes"); -// part1 = bytes(str[0:32]); -// part2 = bytes(str[32:59]); -// -// return (part1, part2); -// } From ac149f7865c38724e3d84c3b96acc4ee1a6958ef Mon Sep 17 00:00:00 2001 From: "David E. Perez Negron R." Date: Wed, 8 May 2024 19:16:34 -0600 Subject: [PATCH 2/3] Some Updates --- foundry.toml | 1 + lib/forge-std | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index b9fe5b5..e474471 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,6 +2,7 @@ src = "src" out = "out" libs = ["lib"] +evm_version = "cancun" [rpc_endpoints] sepolia = "${PROVIDER_URL}" diff --git a/lib/forge-std b/lib/forge-std index 36c303b..5475f85 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 36c303b7ffdd842d06b1ec2744c9b9b5fb3083f3 +Subproject commit 5475f852e3f530d7e25dfb4596aa1f9baa8ffdfc From c39a3cd90bd438cdaece891844bd3c23867e8ccf Mon Sep 17 00:00:00 2001 From: "David E. Perez Negron R." Date: Wed, 8 May 2024 19:18:09 -0600 Subject: [PATCH 3/3] Addding updated version with library for data conversion used in the tests and scripts --- lib/openzeppelin-contracts | 2 +- script/DappIndexer.s.sol | 49 ++++++++------------------- src/CIDStorage.sol | 32 ++++++++++++++++++ src/DappIndexer.sol | 39 ++++++++++++++++++++- test/DappIndexer.t.sol | 69 ++++++++++++++++++++------------------ 5 files changed, 122 insertions(+), 69 deletions(-) create mode 100644 src/CIDStorage.sol diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 01ef448..52c36d4 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 01ef448981be9d20ca85f2faf6ebdf591ce409f3 +Subproject commit 52c36d412e8681053975396223d0ea39687fe33b diff --git a/script/DappIndexer.s.sol b/script/DappIndexer.s.sol index c96cc4d..4e9459b 100644 --- a/script/DappIndexer.s.sol +++ b/script/DappIndexer.s.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.23; import {Script, console2} from "forge-std/Script.sol"; import { DappIndexer} from "src/DappIndexer.sol"; +import { CIDStorage } from "src/CIDStorage.sol"; contract DappIndexerScript is Script { DappIndexer public dappIdxr; @@ -19,63 +20,41 @@ contract DappIndexerScript is Script { uint256 deployerPrivateKey = vm.envUint("PK0"); // startBroadcast and stopBraodcast will let us execute transactions anything between them vm.startBroadcast(deployerPrivateKey); - // here we just need to deploy a new contractc + // here we just need to deploy a new contract dappIdxr = new DappIndexer(owner, owner); console2.log("The Dapps Indexer contract address is: ", address(dappIdxr)); + // Define a Dapp name + bytes32 dapp_name = bytes32("Dapp01"); + + // Define DappURI splited into two bytes32 CIDs bytes32 CID1; bytes32 CID2; - (CID1, CID2) = stringToBytes32Pair(dappsURI[0]); + (CID1, CID2) = CIDStorage.stringToBytes32Pair(dappsURI[0]); // Add Dapp console2.log("-----Adding Dapp-----"); console2.log("Dapp Name is:"); - console2.logBytes32(bytes32("Dapp01")); + console2.logBytes32(dapp_name); console2.log("Bytes32 first:"); console2.logBytes32(CID1); console2.log("Bytes32 second:"); console2.logBytes32(CID2); - console2.log(Bytes32PairToString(CID1, CID2)); - dappIdxr.addDapp(bytes32("Dapp01"), DappIndexer.PackedCID(CID1, CID2)); + console2.log(CIDStorage.bytes32PairToString(CID1, CID2)); + dappIdxr.addDapp(dapp_name, DappIndexer.PackedCID(CID1, CID2)); // retrive Dapp console2.log("-----retriving Dapp-----"); - DappIndexer.PackedCID memory dapp1 = dappIdxr.getDapp(bytes32("Dapp01")); + DappIndexer.PackedCID memory dapp1 = dappIdxr.getDapp(dapp_name); console2.log("Bytes32 first:"); console2.logBytes32(dapp1.CID1); console2.log("Bytes32 second:"); console2.logBytes32(dapp1.CID2); - console2.log(Bytes32PairToString(dapp1.CID1, dapp1.CID2)); + + // reconstruct the CID and verify + console2.log(CIDStorage.bytes32PairToString(dapp1.CID1, dapp1.CID2)); vm.stopBroadcast(); } - - function Bytes32PairToString(bytes32 part1, bytes32 part2) public pure returns (string memory) { - // Concatenate the two bytes32 variables - bytes memory concatenatedBytes = abi.encodePacked(part1, part2); - - // Truncate the concatenated bytes to 59 bytes - bytes memory truncatedBytes = new bytes(59); - for (uint i = 0; i < 59; i++) { - truncatedBytes[i] = concatenatedBytes[i]; - } - - // Convert the truncated bytes to a string - string memory str = string(truncatedBytes); - - return str; - } - - function stringToBytes32Pair(string memory source) public pure returns (bytes32 part1, bytes32 part2) { - bytes memory sourceBytes = bytes(source); - require(sourceBytes.length == 59, "URI string must equal to 59 bytes"); - - assembly { - // Load the first 32 bytes of the string data - part1 := mload(add(sourceBytes, 32)) - // Load the second 32 bytes of the string data - part2 := mload(add(sourceBytes, 64)) - } - } } diff --git a/src/CIDStorage.sol b/src/CIDStorage.sol new file mode 100644 index 0000000..f0db1c1 --- /dev/null +++ b/src/CIDStorage.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +library CIDStorage { + function bytes32PairToString(bytes32 part1, bytes32 part2) public pure returns (string memory) { + // Concatenate the two bytes32 variables + bytes memory concatenatedBytes = abi.encodePacked(part1, part2); + + // Truncate the concatenated bytes to 59 bytes + bytes memory truncatedBytes = new bytes(59); + for (uint i = 0; i < 59; i++) { + truncatedBytes[i] = concatenatedBytes[i]; + } + + // Convert the truncated bytes to a string + string memory str = string(truncatedBytes); + + return str; + } + + function stringToBytes32Pair(string memory source) public pure returns (bytes32 part1, bytes32 part2) { + bytes memory sourceBytes = bytes(source); + require(sourceBytes.length == 59, "URI string must equal to 59 bytes"); + + assembly { + // Load the first 32 bytes of the string data + part1 := mload(add(sourceBytes, 32)) + // Load the second 32 bytes of the string data + part2 := mload(add(sourceBytes, 64)) + } + } +} diff --git a/src/DappIndexer.sol b/src/DappIndexer.sol index 31f07ab..e90ba1a 100644 --- a/src/DappIndexer.sol +++ b/src/DappIndexer.sol @@ -14,6 +14,8 @@ contract DappIndexer is AccessControl { mapping (bytes32 => PackedCID) private dapps; + bytes32[] public dapp_names; + constructor( address defaultAdmin, address moderator @@ -24,14 +26,49 @@ contract DappIndexer is AccessControl { function addDapp(bytes32 dapp_name, PackedCID calldata cid) external onlyRole(MODERATOR_ROLE) { + //if the struct does not exists trace it on the array + if (dapps[dapp_name].CID1 != 0 && dapps[dapp_name].CID2 != 0) { + dapp_names.push(dapp_name); + } + dapps[dapp_name] = PackedCID(cid.CID1, cid.CID2); } - function removeDapp(bytes32 dapp_name) external onlyRole(MODERATOR_ROLE) { + //@Notice: consider calling getDappIndex internal and pop dapps and dapp_names + //@Notice: Yet requiring index might give extra verification + function removeDapp(uint index, bytes32 dapp_name) + external onlyRole(MODERATOR_ROLE) { + require(index < dapp_names.length, "dapp index out of bounds"); + require(dapp_names[index] == dapp_name); + dapp_names[index] = dapp_names[dapp_names.length - 1]; + dapp_names.pop(); delete dapps[dapp_name]; } function getDapp(bytes32 dapp_name) external view returns (PackedCID memory){ return dapps[dapp_name]; } + + function getDappIndex(bytes32 dapp_name) external view returns (uint256 i) { + require(dappExists(dapp_name)); + for(i; i <= dapp_names.length; i++){ + if(dapp_names[i] == dapp_name){ + return i; + } + } + } + + function listDappNames() external view returns (bytes32[] memory) { + return dapp_names; + } + + function dappExists(bytes32 dapp_name) public view returns (bool) { + for(uint i; i <= dapp_names.length; i++){ + if(dapp_names[i] == dapp_name){ + return true; + } + } + return false; + } + } diff --git a/test/DappIndexer.t.sol b/test/DappIndexer.t.sol index 0c067af..aae00ad 100644 --- a/test/DappIndexer.t.sol +++ b/test/DappIndexer.t.sol @@ -3,64 +3,69 @@ pragma solidity ^0.8.23; import { Test, console2 } from "forge-std/Test.sol"; import { DappIndexer } from "src/DappIndexer.sol"; +import { CIDStorage } from "src/CIDStorage.sol"; contract DappIndexerTest is Test { DappIndexer public dappIdxr; string[] public dappsURI; + address public owner; function setUp() public { dappIdxr = new DappIndexer(address(this), address(this)); dappsURI = vm.envString("dapps", ' '); + owner = vm.envAddress("ACC0"); } + // Test adding a dapp that doesn't exist function testAddDapp() public { + bytes32 dapp_name = bytes32("Dapp01"); bytes32 CID1; bytes32 CID2; - (CID1, CID2) = stringToBytes32Pair(dappsURI[0]); + (CID1, CID2) = CIDStorage.stringToBytes32Pair(dappsURI[0]); // Add Dapp console2.log("Dapp Name is:"); - console2.logBytes32(bytes32("Dapp01")); + console2.logBytes32(dapp_name); console2.log("Bytes32 first:"); console2.logBytes32(CID1); console2.log("Bytes32 second:"); console2.logBytes32(CID2); - console2.log(Bytes32PairToString(CID1, CID2)); - dappIdxr.addDapp(bytes32("Dapp01"), DappIndexer.PackedCID(CID1, CID2)); + console2.log(CIDStorage.bytes32PairToString(CID1, CID2)); + dappIdxr.addDapp(dapp_name, DappIndexer.PackedCID(CID1, CID2)); + // retrive Dapp - DappIndexer.PackedCID memory dapp1 = dappIdxr.getDapp(bytes32("Dapp01")); + DappIndexer.PackedCID memory dapp1 = dappIdxr.getDapp(dapp_name); assertEq(CID1, dapp1.CID1); assertEq(CID2, dapp1.CID2); - string memory retrivedDappURI = Bytes32PairToString(dapp1.CID1, dapp1.CID2); + + // reconstruct the CID and verify + string memory retrivedDappURI = CIDStorage.bytes32PairToString(dapp1.CID1, dapp1.CID2); assertEq(dappsURI[0], retrivedDappURI); } - function Bytes32PairToString(bytes32 part1, bytes32 part2) public pure returns (string memory) { - // Concatenate the two bytes32 variables - bytes memory concatenatedBytes = abi.encodePacked(part1, part2); +//## ToDo ISSUE #2 +// +//### Features testing +// +//- [x] test adding a dapp and retrive it. (addDapp, getDapp) +// +//- [ ] test removing a dapp that exists (verify dapps and dapp_names) +//- [ ] test dappExists (lower, upper, fuzz) +//- [ ] test removing a dapp that doesn't exist +//- [ ] test getting that names (listDappNames) +//- [ ] test getDappIndex (lower, upper, fuzz) +//- [ ] check if dapp name exists +// +//### Library testing +// +//- [ ] test adding longer CID +//- [ ] test adding shorter CID +//- [ ] test fuzz +// +//### Testing Roles Open Zeppelin Module +// +//- [ ] test roles for educational purposes. +// - // Truncate the concatenated bytes to 59 bytes - bytes memory truncatedBytes = new bytes(59); - for (uint i = 0; i < 59; i++) { - truncatedBytes[i] = concatenatedBytes[i]; - } - - // Convert the truncated bytes to a string - string memory str = string(truncatedBytes); - - return str; - } - - function stringToBytes32Pair(string memory source) public pure returns (bytes32 part1, bytes32 part2) { - bytes memory sourceBytes = bytes(source); - require(sourceBytes.length == 59, "URI string must equal to 59 bytes"); - - assembly { - // Load the first 32 bytes of the string data - part1 := mload(add(sourceBytes, 32)) - // Load the second 32 bytes of the string data - part2 := mload(add(sourceBytes, 64)) - } - } }