main
cyl19970726 2 years ago
parent 31dc57a57b
commit 5bbba6799a

@ -80,14 +80,19 @@ library EnumerableSet {
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _replace(Set storage set, uint256 index, bytes32 oldValue , bytes32 newValue) private returns (bool) {
if (set._values[index-1] != oldValue){
function _replace(
Set storage set,
uint256 index,
bytes32 oldValue,
bytes32 newValue
) private returns (bool) {
if (set._values[index - 1] != oldValue) {
return false;
}
set._indexes[oldValue] = 0;
// 0 represents the value is empty
set._indexes[newValue] = index;
set._values[index-1] = newValue;
set._values[index - 1] = newValue;
return true;
}
@ -134,7 +139,10 @@ library EnumerableSet {
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
function _contains(
Set storage set,
bytes32 value
) private view returns (bool) {
return set._indexes[value] != 0;
}
@ -155,7 +163,10 @@ library EnumerableSet {
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
function _at(
Set storage set,
uint256 index
) private view returns (bytes32) {
return set._values[index];
}
@ -183,7 +194,10 @@ library EnumerableSet {
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
function add(
Bytes32Set storage set,
bytes32 value
) internal returns (bool) {
return _add(set._inner, value);
}
@ -193,14 +207,20 @@ library EnumerableSet {
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
function remove(
Bytes32Set storage set,
bytes32 value
) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
function contains(
Bytes32Set storage set,
bytes32 value
) internal view returns (bool) {
return _contains(set._inner, value);
}
@ -221,7 +241,10 @@ library EnumerableSet {
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
function at(
Bytes32Set storage set,
uint256 index
) internal view returns (bytes32) {
return _at(set._inner, index);
}
@ -233,7 +256,9 @@ library EnumerableSet {
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
function values(
Bytes32Set storage set
) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
@ -257,12 +282,26 @@ library EnumerableSet {
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
function add(
AddressSet storage set,
address value
) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function replace(AddressSet storage set,uint256 index, address oldValue, address newValue) internal returns (bool) {
return _replace(set._inner, index ,bytes32(uint256(uint160(oldValue))), bytes32(uint256(uint160(newValue))));
function replace(
AddressSet storage set,
uint256 index,
address oldValue,
address newValue
) internal returns (bool) {
return
_replace(
set._inner,
index,
bytes32(uint256(uint160(oldValue))),
bytes32(uint256(uint160(newValue)))
);
}
/**
@ -271,14 +310,20 @@ library EnumerableSet {
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
function remove(
AddressSet storage set,
address value
) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
function contains(
AddressSet storage set,
address value
) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
@ -299,7 +344,10 @@ library EnumerableSet {
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
function at(
AddressSet storage set,
uint256 index
) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
@ -311,7 +359,9 @@ library EnumerableSet {
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
function values(
AddressSet storage set
) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
@ -345,14 +395,20 @@ library EnumerableSet {
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
function remove(
UintSet storage set,
uint256 value
) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
function contains(
UintSet storage set,
uint256 value
) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
@ -373,7 +429,10 @@ library EnumerableSet {
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
function at(
UintSet storage set,
uint256 index
) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
@ -385,7 +444,9 @@ library EnumerableSet {
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
function values(
UintSet storage set
) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;

@ -1,166 +0,0 @@
pragma solidity ^0.8.0;
// import "hardhat/console.sol";
// import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "./EnumerableSet.sol";
import "./Repositorylib.sol";
import "./database/database.sol";
contract Hub is AccessControlEnumerableUpgradeable{
using EnumerableSet for EnumerableSet.AddressSet;
using RepositoryLib for RepositoryLib.BranchInfo;
// Hub Info
bytes32 public constant CREATOR = bytes32(uint256(0));
bytes32 public constant MANAGER = bytes32(uint256(1));
bytes32 public constant CONTRIBUTOR = bytes32(uint256(2));
bytes32[] public RoleList = [CREATOR,MANAGER,CONTRIBUTOR];
bool public permissionless;
// Repository Info
struct RepositoryInfo{
uint256 repoNameIndex;
address owner;
bool exist;
EnumerableSet.AddressSet repoContributors;
RepositoryLib.BranchInfo branchs;
}
mapping(bytes=>RepositoryInfo) nameToRepository;
bytes[] public repoNames;
// DataBase Info
database public db;
// ===== hub operator functions======
function openPermissonlessJoin(bool open) public {
require(hasRole(CREATOR, _msgSender()));
permissionless = open;
}
function memberShip() public view returns(bool){
if (hasRole(CREATOR, _msgSender())){
return true;
}else if (hasRole(MANAGER, _msgSender())){
return true;
}else if (hasRole(CONTRIBUTOR, _msgSender())){
return true;
}else{
return false;
}
}
//createRepository can be invoked by anyone within Hub
function createRepository(bytes memory repoName) public{
require(memberShip());
require(
repoName.length > 0 && repoName.length <= 100,
"RepoName length must be 1-100"
);
for (uint i; i < repoName.length; i++) {
bytes1 char = repoName[i];
require(
(char >= 0x61 && char <= 0x7A) || //a-z
(char >= 0x41 && char <= 0x5A) || //A-Z
(char >= 0x30 && char <= 0x39) || //0-9
(char == 0x2D || char == 0x2E || char == 0x5F), //-._
"RepoName must be alphanumeric or -._"
);
}
RepositoryInfo storage repo = nameToRepository[repoName];
require(
repo.exist == false ,
"RepoName already exist"
);
repo.repoNameIndex = repoNames.length;
repo.owner = _msgSender();
repo.exist = true;
repoNames.push(repoName);
}
function deleteRepository(bytes memory repoName) public returns(address){
require(hasRole(CREATOR, _msgSender()) || hasRole(MANAGER, _msgSender()));
require(nameToRepository[repoName].exist==true,"repoName do not exist");
delete(nameToRepository[repoName]);
}
function addMember( bytes32 senderRole , bytes32 role,address member) public{
require(hasRole(senderRole, _msgSender()));
require(senderRole>role);
grantRole(role, member);
}
function deleteMember(bytes32 senderRole ,bytes32 role,address member) public{
require(hasRole(senderRole, _msgSender()));
require(senderRole>role);
revokeRole(role, member);
}
// permissionlessJoin can be invoked by everyone who want to join this hub
function permissionlessJoin() public{
require(permissionless,"permissionless join no open");
grantRole(CONTRIBUTOR, _msgSender());
}
// ===== repository operator functions======
function isRepoContributor(bytes memory repoName , address member) internal view returns(bool){
RepositoryInfo storage repo = nameToRepository[repoName];
if (repo.owner == member) return true;
if (repo.repoContributors.contains(member)) {
return true;
}else {
return false;
}
}
function listRepoBranchs( bytes memory repoName)external view {
nameToRepository[repoName].branchs.listBranchs();
}
function updateRepoBranch(
bytes memory repoName,
bytes memory branchPath,
bytes20 refHash
)external{
require(isRepoContributor(repoName, _msgSender()));
nameToRepository[repoName].branchs.updateBranch(repoName,branchPath,refHash);
}
function removeRepoBranch(
bytes memory repoName,
bytes memory branchPath
) external{
require(isRepoContributor(repoName, _msgSender()));
nameToRepository[repoName].branchs.removeBranch(repoName,branchPath);
}
// ===== database operator functions======
function newDataBase() external onlyRole(CREATOR) {
}
function download(
bytes memory repoName,
bytes memory path
) external view returns (bytes memory, bool) {
// call flat directory(FD)
return db.download(repoName, path);
}
function upload(
bytes memory repoName,
bytes memory path,
bytes calldata data
) external payable {
return db.upload(repoName, path,data);
}
}

@ -0,0 +1,32 @@
pragma solidity ^0.8.0;
// import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "./Hubv3.sol";
contract HubFactory is Ownable {
event CreateHub(address indexed hub, address indexed creator);
address[] public hubs;
Hubv3 public hubImp;
// function initialize() initializer public {
// __Ownable_init();
// }
function newHubImp() public onlyOwner {
hubImp = new Hubv3();
}
function setHubImp(address addr) public onlyOwner {
hubImp = Hubv3(addr);
}
function createHub(bool dbSelector) external {
address instance = Clones.clone(address(hubImp));
hubs.push(instance);
Hubv3(instance).initialize(dbSelector, _msgSender());
emit CreateHub(instance, _msgSender());
}
}

@ -0,0 +1,256 @@
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./EnumerableSet.sol";
import "./Repolib.sol";
import "./database/database.sol";
import "./database/ethstorage.sol";
import "./database/filecoin.sol";
contract Hubv3 is AccessControl, Initializable {
using EnumerableSet for EnumerableSet.AddressSet;
using Repolib for Repolib.BranchInfo;
// Hub Info
// bytes32 public constant CREATOR = bytes32(uint256(1));
bytes32 public constant MANAGER = bytes32(uint256(1));
bytes32 public constant CONTRIBUTOR = bytes32(uint256(2));
bytes32 public constant NOTFOUND = bytes32(uint256(10000));
bytes32[] public RoleList = [DEFAULT_ADMIN_ROLE, MANAGER, CONTRIBUTOR];
bool public permissionless;
// Repository Info
struct RepositoryInfo {
uint256 repoNameIndex;
address owner;
bool exist;
EnumerableSet.AddressSet repoContributors;
Repolib.BranchInfo branchs;
}
mapping(bytes => RepositoryInfo) nameToRepository;
bytes[] public repoNames;
// DataBase Info
database public db;
constructor() {
_setupRole(AccessControl.DEFAULT_ADMIN_ROLE, _msgSender());
_setupRole(MANAGER, _msgSender());
_setRoleAdmin(CONTRIBUTOR, MANAGER);
}
function initialize(bool dbSelector, address user) public initializer {
_setupRole(AccessControl.DEFAULT_ADMIN_ROLE, user);
_setupRole(MANAGER, user);
_setRoleAdmin(CONTRIBUTOR, MANAGER);
_newDataBase(dbSelector);
}
// ===== hub operator functions======
function openPermissonlessJoin(bool open) public {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()));
permissionless = open;
}
function memberRole(
address member
) public view returns (bool IsAdmin, bool IsManager, bool IsContributor) {
if (hasRole(DEFAULT_ADMIN_ROLE, member)) {
IsAdmin = true;
}
if (hasRole(MANAGER, member)) {
IsManager = true;
}
if (hasRole(CONTRIBUTOR, member)) {
IsContributor = true;
}
return (IsAdmin, IsManager, IsContributor);
}
function membership(address member) public view returns (bool) {
if (hasRole(DEFAULT_ADMIN_ROLE, member)) {
return true;
}
if (hasRole(MANAGER, member)) {
return true;
}
if (hasRole(CONTRIBUTOR, member)) {
return true;
}
return false;
}
function addManager(address member) public {
grantRole(MANAGER, member);
}
function addContributor(address member) public {
grantRole(CONTRIBUTOR, member);
}
function removeManager(address member) public {
revokeRole(MANAGER, member);
}
function removeContributor(address member) public {
revokeRole(CONTRIBUTOR, member);
}
// permissionlessJoin can be invoked by everyone who want to join this hub
function permissionlessJoin() public {
require(permissionless, "permissionless join no open");
grantRole(CONTRIBUTOR, _msgSender());
}
// ===== repository operator functions======
//createRepository can be invoked by anyone within Hub
function createRepo(bytes memory repoName) public {
require(membership(_msgSender()));
require(
repoName.length > 0 && repoName.length <= 100,
"RepoName length must be 1-100"
);
for (uint i; i < repoName.length; i++) {
bytes1 char = repoName[i];
require(
(char >= 0x61 && char <= 0x7A) || //a-z
(char >= 0x41 && char <= 0x5A) || //A-Z
(char >= 0x30 && char <= 0x39) || //0-9
(char == 0x2D || char == 0x2E || char == 0x5F), //-._
"RepoName must be alphanumeric or -._"
);
}
RepositoryInfo storage repo = nameToRepository[repoName];
require(repo.exist == false, "RepoName already exist");
repo.repoNameIndex = repoNames.length;
repo.owner = _msgSender();
repo.exist = true;
repoNames.push(repoName);
}
function deleteRepo(bytes memory repoName) public {
require(
hasRole(DEFAULT_ADMIN_ROLE, _msgSender()) ||
hasRole(MANAGER, _msgSender())
);
require(
nameToRepository[repoName].exist == true,
"repoName do not exist"
);
delete (nameToRepository[repoName]);
}
function repoOwner(bytes memory repoName) public view returns (address) {
RepositoryInfo storage repo = nameToRepository[repoName];
return repo.owner;
}
function repoContributors(
bytes memory repoName
) public view returns (address[] memory) {
RepositoryInfo storage repo = nameToRepository[repoName];
return repo.repoContributors.values();
}
function isRepoMembership(
bytes memory repoName,
address member
) internal view returns (bool) {
RepositoryInfo storage repo = nameToRepository[repoName];
if (repo.owner == member) return true;
if (repo.repoContributors.contains(member)) {
return true;
} else {
return false;
}
}
function addRepoContributor(
bytes memory repoName,
address con
) public returns (bool) {
RepositoryInfo storage repo = nameToRepository[repoName];
require(_msgSender() == repo.owner, "only repo owner");
return nameToRepository[repoName].repoContributors.add(con);
}
function removeRepoContributor(
bytes memory repoName,
address con
) public returns (bool) {
RepositoryInfo storage repo = nameToRepository[repoName];
require(_msgSender() == repo.owner, "only repo owner");
return nameToRepository[repoName].repoContributors.remove(con);
}
// listRef
function listRepoRefs(
bytes memory repoName
) external view returns (Repolib.refData[] memory list) {
return nameToRepository[repoName].branchs.listBranchs();
}
// setRef
function setRepoRef(
bytes memory repoName,
bytes memory branchPath,
bytes20 refHash
) external {
require(isRepoMembership(repoName, _msgSender()));
nameToRepository[repoName].branchs.updateBranch(
repoName,
branchPath,
refHash
);
}
function getRepoRef(
bytes memory repoName,
bytes memory branchPath
) external view returns (bytes20) {
return
nameToRepository[repoName].branchs.getBranch(repoName, branchPath);
}
function delRepoRef(
bytes memory repoName,
bytes memory branchPath
) external {
require(isRepoMembership(repoName, _msgSender()));
nameToRepository[repoName].branchs.removeBranch(repoName, branchPath);
}
// ===== database operator functions======
function newDataBase(bool flag) public onlyRole(DEFAULT_ADMIN_ROLE) {
_newDataBase(flag);
}
function _newDataBase(bool flag) internal {
if (flag) {
db = new ethstorage();
} else {
db = new filecoin();
}
}
function download(
bytes memory repoName,
bytes memory path
) external view returns (bytes memory, bool) {
// call flat directory(FD)
return db.download(repoName, path);
}
function upload(
bytes memory repoName,
bytes memory path,
bytes calldata data
) external payable {
return db.upload(repoName, path, data);
}
}

@ -1,7 +1,6 @@
pragma solidity ^0.8.0;
library RepositoryLib {
library Repolib {
struct refInfo {
bytes20 hash;
uint96 index;
@ -12,13 +11,20 @@ library RepositoryLib {
bytes name;
}
struct BranchInfo{
struct BranchInfo {
mapping(bytes => refInfo) branchToRefInfo; // dev => {hash: 0x1234..., index: 1 }
bytes[] branchs; // branch,reference
}
bytes[] branchs; //
}
function branchNum(
BranchInfo storage info
) internal view returns (uint256) {
return info.branchs.length;
}
function listBranchs(BranchInfo storage info) external view returns (refData[] memory list) {
function listBranchs(
BranchInfo storage info
) internal view returns (refData[] memory list) {
list = new refData[](info.branchs.length);
for (uint index = 0; index < info.branchs.length; index++) {
list[index] = _convertToRefData(
@ -33,32 +39,40 @@ library RepositoryLib {
bytes memory repositoryName,
bytes memory branch,
bytes20 refHash
) external {
bytes memory fullname = bytes.concat(repositoryName,"/",branch);
require(refHash!=bytes20(0),"reference hash don't allow to set 0x0" );
if (info.branchToRefInfo[fullname].hash==bytes20(0)) {
) internal {
bytes memory fullname = bytes.concat(repositoryName, "/", branch);
require(refHash != bytes20(0), "reference hash don't allow to set 0x0");
if (info.branchToRefInfo[fullname].hash == bytes20(0)) {
info.branchToRefInfo[fullname].hash = refHash;
info.branchToRefInfo[fullname].index = uint96(info.branchs.length);
info.branchs.push(fullname);
}else {
} else {
info.branchToRefInfo[fullname].hash = refHash;
}
}
function getBranch(
BranchInfo storage info,
bytes memory repositoryName,
bytes memory branch
) internal view returns (bytes20) {
bytes memory fullname = bytes.concat(repositoryName, "/", branch);
return info.branchToRefInfo[fullname].hash;
}
function removeBranch(
BranchInfo storage info,
bytes memory repositoryName,
bytes memory branch
) external {
bytes memory fullname = bytes.concat(repositoryName,"/",branch);
refInfo memory refI = info.branchToRefInfo[fullname];
) internal {
bytes memory fullname = bytes.concat(repositoryName, "/", branch);
refInfo memory refI = info.branchToRefInfo[fullname];
require(
refI.hash != bytes20(0),
"Reference of this name does not exist"
);
uint256 lastIndex = info.branchs.length -1 ;
if (refI.index < lastIndex){
uint256 lastIndex = info.branchs.length - 1;
if (refI.index < lastIndex) {
info.branchToRefInfo[info.branchs[lastIndex]].index = refI.index;
info.branchs[refI.index] = info.branchs[lastIndex];
}
@ -73,5 +87,4 @@ library RepositoryLib {
res.hash = rInfo.hash;
res.name = info.branchs[rInfo.index];
}
}
}

@ -1,16 +1,14 @@
pragma solidity ^0.8.0;
interface database {
function download(
bytes memory repoName,
bytes memory path
) external view returns (bytes memory, bool) ;
) external view returns (bytes memory, bool);
function upload(
bytes memory repoName,
bytes memory path,
bytes calldata data
) external payable;
}
}

@ -1,6 +1,8 @@
pragma solidity ^0.8.0;
import "./EthStorage/LargeStorageManagerV2.sol";
contract EthStorage is LargeStorageManagerV2{
import "./database.sol";
contract ethstorage is LargeStorageManagerV2, database {
function stakeTokens(
bytes memory repoName,
bytes memory path
@ -31,7 +33,7 @@ contract EthStorage is LargeStorageManagerV2{
function download(
bytes memory repoName,
bytes memory path
) external view returns (bytes memory, bool) {
) external view override returns (bytes memory, bool) {
// call flat directory(FD)
return _get(keccak256(bytes.concat(repoName, "/", path)));
}
@ -40,7 +42,7 @@ contract EthStorage is LargeStorageManagerV2{
bytes memory repoName,
bytes memory path,
bytes calldata data
) external payable {
) external payable override {
_putChunkFromCalldata(
keccak256(bytes.concat(repoName, "/", path)),
0,
@ -54,7 +56,7 @@ contract EthStorage is LargeStorageManagerV2{
bytes memory path,
uint256 chunkId,
bytes calldata data
) external payable{
) external payable {
_putChunkFromCalldata(
keccak256(bytes.concat(repoName, "/", path)),
chunkId,
@ -63,10 +65,7 @@ contract EthStorage is LargeStorageManagerV2{
);
}
function remove(
bytes memory repoName,
bytes memory path
) external {
function remove(bytes memory repoName, bytes memory path) external {
// The actually process of remove will remove all the chunks
_remove(keccak256(bytes.concat(repoName, "/", path)), 0);
}
@ -75,7 +74,7 @@ contract EthStorage is LargeStorageManagerV2{
bytes memory repoName,
bytes memory path,
uint256 chunkId
) external {
) external {
_removeChunk(keccak256(bytes.concat(repoName, "/", path)), chunkId);
}
@ -92,4 +91,4 @@ contract EthStorage is LargeStorageManagerV2{
) external view returns (uint256) {
return _countChunks(keccak256(bytes.concat(repoName, "/", name)));
}
}
}

@ -1,27 +1,25 @@
pragma solidity ^0.8.0;
contract filecoin{
import "./database.sol";
contract filecoin is database {
mapping(bytes32 => bytes) public pathToHash;
function download(
function download(
bytes memory repoName,
bytes memory path
) external view returns (bytes memory, bool) {
bytes32 fullName = keccak256(bytes.concat(repoName, "/", path));
) external view override returns (bytes memory, bool) {
bytes32 fullName = keccak256(bytes.concat(repoName, "/", path));
// call flat directory(FD)
return (pathToHash[fullName],true);
return (pathToHash[fullName], true);
}
function upload(
bytes memory repoName,
bytes memory path,
bytes calldata data
) external payable {
bytes32 fullName = keccak256(bytes.concat(repoName, "/", path));
) external payable override {
bytes32 fullName = keccak256(bytes.concat(repoName, "/", path));
pathToHash[fullName] = data;
}
}
}

Loading…
Cancel
Save