feat: suppot multiple repos

main
cyl19970726 2 years ago
parent 6823e4df65
commit 8a76ef124e

@ -21,13 +21,14 @@ contract Git3 is LargeStorageManager {
mapping(bytes => address) public repoNameToOwner; mapping(bytes => address) public repoNameToOwner;
mapping(bytes => refInfo) public nameToRefInfo; // dev => {hash: 0x1234..., index: 1 } mapping(bytes => refInfo) public nameToRefInfo; // dev => {hash: 0x1234..., index: 1 }
bytes[] public refs; // [main, dev, test, staging] mapping(bytes => bytes[]) public repoNameToRefs; // [main, dev, test, staging]
function _convertRefInfo( function _convertRefInfo(
bytes memory repoName,
refInfo memory info refInfo memory info
) internal view returns (refData memory res) { ) internal view returns (refData memory res) {
res.hash = info.hash; res.hash = info.hash;
res.name = refs[info.index]; res.name = repoNameToRefs[repoName][info.index];
} }
constructor() LargeStorageManager(0) {} constructor() LargeStorageManager(0) {}
@ -45,20 +46,16 @@ contract Git3 is LargeStorageManager {
return _get(keccak256(bytes.concat(repoName, "/", path))); return _get(keccak256(bytes.concat(repoName, "/", path)));
} }
function _createRepo(bytes memory repoName) internal { function createRepo(bytes memory repoName) external{
if (repoNameToOwner[repoName] == address(0)) { require(repoNameToOwner[repoName] == address(0),"RepoName already exist");
repoNameToOwner[repoName] = msg.sender; repoNameToOwner[repoName] = msg.sender;
} else {
require(repoNameToOwner[repoName] == msg.sender, "only owner");
}
} }
function upload( function upload(
bytes memory repoName, bytes memory repoName,
bytes memory path, bytes memory path,
bytes calldata data bytes calldata data
) external payable { ) external payable onlyOwner(repoName){
_createRepo(repoName);
_putChunkFromCalldata( _putChunkFromCalldata(
keccak256(bytes.concat(repoName, "/", path)), keccak256(bytes.concat(repoName, "/", path)),
0, 0,
@ -72,8 +69,7 @@ contract Git3 is LargeStorageManager {
bytes memory path, bytes memory path,
uint256 chunkId, uint256 chunkId,
bytes calldata data bytes calldata data
) external payable { ) external payable onlyOwner(repoName){
_createRepo(repoName);
_putChunkFromCalldata( _putChunkFromCalldata(
keccak256(bytes.concat(repoName, "/", path)), keccak256(bytes.concat(repoName, "/", path)),
chunkId, chunkId,
@ -104,11 +100,10 @@ contract Git3 is LargeStorageManager {
return _countChunks(keccak256(bytes.concat(repoName, "/", name))); return _countChunks(keccak256(bytes.concat(repoName, "/", name)));
} }
function listRefs() public view returns (refData[] memory list) { function listRefs(bytes memory repoName) public view returns (refData[] memory list) {
// todo: Differentiate all refs corresponding to a repo list = new refData[](repoNameToRefs[repoName].length);
list = new refData[](refs.length); for (uint index = 0; index < repoNameToRefs[repoName].length; index++) {
for (uint index = 0; index < refs.length; index++) { list[index] = _convertRefInfo(repoName,nameToRefInfo[repoNameToRefs[repoName][index]]);
list[index] = _convertRefInfo(nameToRefInfo[refs[index]]);
} }
} }
@ -116,14 +111,12 @@ contract Git3 is LargeStorageManager {
bytes memory repoName, bytes memory repoName,
bytes memory name, bytes memory name,
bytes20 refHash bytes20 refHash
) public { ) public onlyOwner(repoName){
bytes memory fullName = bytes.concat(repoName, "/", name); bytes memory fullName = bytes.concat(repoName, "/", name);
// only execute `sload` once to reduce gas consumption // only execute `sload` once to reduce gas consumption
refInfo memory srs; refInfo memory srs;
srs = nameToRefInfo[fullName]; srs = nameToRefInfo[fullName];
uint256 refsLen = refs.length; uint256 refsLen = repoNameToRefs[repoName].length;
_createRepo(repoName);
if (srs.hash == bytes20(0)) { if (srs.hash == bytes20(0)) {
// store refHash for the first time // store refHash for the first time
@ -135,7 +128,7 @@ contract Git3 is LargeStorageManager {
nameToRefInfo[fullName].hash = refHash; nameToRefInfo[fullName].hash = refHash;
nameToRefInfo[fullName].index = uint96(refsLen); nameToRefInfo[fullName].index = uint96(refsLen);
refs.push(fullName); repoNameToRefs[repoName].push(fullName);
} else { } else {
// only update refHash // only update refHash
nameToRefInfo[fullName].hash = refHash; nameToRefInfo[fullName].hash = refHash;
@ -150,7 +143,7 @@ contract Git3 is LargeStorageManager {
// only execute `sload` once to reduce gas consumption // only execute `sload` once to reduce gas consumption
refInfo memory srs; refInfo memory srs;
srs = nameToRefInfo[fullName]; srs = nameToRefInfo[fullName];
uint256 refsLen = refs.length; uint256 refsLen = repoNameToRefs[repoName].length;
require( require(
srs.hash != bytes20(0), srs.hash != bytes20(0),
@ -159,10 +152,10 @@ contract Git3 is LargeStorageManager {
require(srs.index < refsLen, "System Error: Invalid index"); require(srs.index < refsLen, "System Error: Invalid index");
if (srs.index < refsLen - 1) { if (srs.index < refsLen - 1) {
refs[srs.index] = refs[refsLen - 1]; repoNameToRefs[repoName][srs.index] = repoNameToRefs[repoName][refsLen - 1];
nameToRefInfo[refs[refsLen - 1]].index = srs.index; nameToRefInfo[repoNameToRefs[repoName][refsLen - 1]].index = srs.index;
} }
refs.pop(); repoNameToRefs[repoName].pop();
delete nameToRefInfo[fullName]; delete nameToRefInfo[fullName];
} }
} }

@ -13,7 +13,8 @@ describe("Git3 Test", function () {
let singer; let singer;
[singer] = await ethers.getSigners(); [singer] = await ethers.getSigners();
console.log("singer", singer.address);
await git3.createRepo("0x11");
await git3.upload("0x11", "0x616263", "0x112233"); await git3.upload("0x11", "0x616263", "0x112233");
expect(await git3.download("0x11", "0x616263")).to.eql(["0x112233", true]); expect(await git3.download("0x11", "0x616263")).to.eql(["0x112233", true]);
@ -39,6 +40,8 @@ describe("Git3 Test", function () {
const git3 = await Git3.deploy(); const git3 = await Git3.deploy();
await git3.deployed(); await git3.deployed();
await git3.createRepo("0x11");
expect(await git3.countChunks("0x11", "0x616263")).to.eql(ToBig(0)); expect(await git3.countChunks("0x11", "0x616263")).to.eql(ToBig(0));
let data0 = Array.from({ length: 10 }, () => let data0 = Array.from({ length: 10 }, () =>
@ -71,6 +74,8 @@ describe("Git3 Test", function () {
await git3.deployed(); await git3.deployed();
let repoName = "0x11"; let repoName = "0x11";
await git3.createRepo(repoName);
function concatHexStr(s1, s2) { function concatHexStr(s1, s2) {
return s1.concat("2f").concat(s2.slice(2)); return s1.concat("2f").concat(s2.slice(2));
} }
@ -87,7 +92,7 @@ describe("Git3 Test", function () {
let data2 = "0xcccccccccccccccccccccccccccccccccccccccc"; let data2 = "0xcccccccccccccccccccccccccccccccccccccccc";
await git3.setRef(repoName, key2, data2); await git3.setRef(repoName, key2, data2);
let refs = await git3.listRefs(); let refs = await git3.listRefs(repoName);
expect(refs[0]).to.eql([data0, concatHexStr(repoName, key0)]); expect(refs[0]).to.eql([data0, concatHexStr(repoName, key0)]);
expect(refs[1]).to.eql([data1, concatHexStr(repoName, key1)]); expect(refs[1]).to.eql([data1, concatHexStr(repoName, key1)]);
expect(refs[2]).to.eql([data2, concatHexStr(repoName, key2)]); expect(refs[2]).to.eql([data2, concatHexStr(repoName, key2)]);
@ -95,20 +100,43 @@ describe("Git3 Test", function () {
// check delRef // check delRef
await git3.delRef(repoName, key0); await git3.delRef(repoName, key0);
refs = await git3.listRefs(); refs = await git3.listRefs(repoName);
expect(refs[0]).to.eql([data2, concatHexStr(repoName, key2)]); expect(refs[0]).to.eql([data2, concatHexStr(repoName, key2)]);
expect(refs[1]).to.eql([data1, concatHexStr(repoName, key1)]); expect(refs[1]).to.eql([data1, concatHexStr(repoName, key1)]);
expect(refs.length).to.eql(2); expect(refs.length).to.eql(2);
await git3.delRef(repoName, key1); await git3.delRef(repoName, key1);
refs = await git3.listRefs(); refs = await git3.listRefs(repoName);
expect(refs[0]).to.eql([data2, concatHexStr(repoName, key2)]); expect(refs[0]).to.eql([data2, concatHexStr(repoName, key2)]);
expect(refs.length).to.eql(1); expect(refs.length).to.eql(1);
// check update // check update
let data3 = "0xdddddddddddddddddddddddddddddddddddddddd"; let data3 = "0xdddddddddddddddddddddddddddddddddddddddd";
await git3.setRef(repoName, key2, data3); await git3.setRef(repoName, key2, data3);
refs = await git3.listRefs(); refs = await git3.listRefs(repoName);
expect(refs[0]).to.eql([data3, concatHexStr(repoName, key2)]); expect(refs[0]).to.eql([data3, concatHexStr(repoName, key2)]);
}); });
it("Access Control", async function () {
const Git3 = await ethers.getContractFactory("Git3");
const git3 = await Git3.deploy();
await git3.deployed();
let singer;
let user1;
[singer,user1,] = await ethers.getSigners();
await git3.connect(singer).createRepo("0x11");
await expect(git3.connect(user1).upload("0x11", "0x616263", "0x112233")).to.be.revertedWith("only owner");
await expect(git3.connect(user1).uploadChunk("0x11", "0x616263", 0,"0x112233")).to.be.revertedWith("only owner");
await expect(git3.connect(user1).setRef("0x11", "0x616263", "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")).to.be.revertedWith("only owner");
await git3.connect(singer).upload("0x11", "0x616263", "0x112233")
expect(await git3.download("0x11", "0x616263")).to.eql(["0x112233", true]);
await git3.connect(singer).setRef("0x11", "0x616263", "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
await expect(git3.connect(user1).remove("0x11", "0x616263")).to.be.revertedWith("only owner");
await expect(git3.connect(user1).delRef("0x11", "0x616263")).to.be.revertedWith("only owner");
});
}); });

Loading…
Cancel
Save