Skip to content

Commit

Permalink
Add invalid tag name check, add tag revert and replace logic
Browse files Browse the repository at this point in the history
  • Loading branch information
weiqiushi committed Sep 15, 2024
1 parent 5a00437 commit a0596bd
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 14 deletions.
97 changes: 92 additions & 5 deletions contracts/data_tag.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,32 @@ contract DataTag is Initializable, UUPSUpgradeable, OwnableUpgradeable {
return keccak256(abi.encodePacked(fullName));
}

function isCharValid(uint8 ele) view internal returns (bool) {
uint8[1] memory invalidChars = [47]; // 47是"/"的ASCII码

for (uint i = 0; i < invalidChars.length; i++) {
if (invalidChars[i] == ele) {
return true;
}
}
return false;
}

function isNameValid(string calldata name) view public returns (bool) {
bytes memory b = bytes(name);
if (b.length == 0) {
return false;
}

for (uint i = 0; i < b.length; i++) {
if (isCharValid(uint8(b[i]))) {
return false;
}
}

return true;
}

/**
* 设置一个tag的描述。设置描述也等于自己对这个描述点赞
* @param new_tags tag的全路径,比如["a", "b", "c"]表示一个tag的全路径是a/b/c
Expand All @@ -88,7 +114,7 @@ contract DataTag is Initializable, UUPSUpgradeable, OwnableUpgradeable {
bytes32 parent = bytes32(0);
string memory fullName = "";
for (uint i = 0; i < new_tags.length; i++) {
require(bytes(new_tags[i]).length != 0, "empty name");
require(isNameValid(new_tags[i]), "invalid name");

fullName = string.concat(fullName, "/", new_tags[i]);
bytes32 tagHash = keccak256(abi.encodePacked(fullName));
Expand Down Expand Up @@ -157,6 +183,51 @@ contract DataTag is Initializable, UUPSUpgradeable, OwnableUpgradeable {
_rateTagMeta(tagHash, from, like);
}

// check if a is parent of b
function isParent(bytes32 a, bytes32 b) public view returns (bool) {
if (a == bytes32(0)) {
return true;
}
bytes32 p = tags[b].parent;
while (p != bytes32(0)) {
if (p == a) {
return true;
}
p = tags[p].parent;
}
return false;
}

// check if a is child of b
// bytes(0)是任何tag的parent,这种处理可能有助于以后tag的删除和数组空位的重新利用
function isChild(bytes32 a, bytes32 b) public view returns (bool) {
if (b == bytes32(0)) {
return true;
}
uint layers = 0;
bytes32 p = tags[a].parent;
while (p != bytes32(0)) {
p = tags[p].parent;
layers++;
}

bytes32[] memory path = new bytes32[](layers);
p = tags[a].parent;
for (uint i = 0; i < layers; i++) {
path[i] = p;
p = tags[p].parent;
}
// path is a`s all parent hash

// check b is in path
for (uint i = 0; i < path.length; i++) {
if (path[i] == b) {
return true;
}
}
return false;
}

/**
* 给数据附加tag。可以一次性附加多个, 先实现成tag必须存在才能附加
* @param dataHash 数据的hash
Expand All @@ -169,17 +240,33 @@ contract DataTag is Initializable, UUPSUpgradeable, OwnableUpgradeable {
for (uint i = 0; i < data_tags.length; i++) {
bytes32 tagHash = data_tags[i];
require(bytes(tags[tagHash].name).length != 0, "tag not exist");

// 检查tagHash是否是已存在的某个tag的parent,或者某个tag的child
bytes32 oldTag = bytes32(0);
for (uint j = 0; j < datas[dataHash][msg.sender].tags.length; j++) {
require(!isParent(tagHash, datas[dataHash][msg.sender].tags[j]), "child tag exist");

if (isChild(tagHash, datas[dataHash][msg.sender].tags[j])) {
oldTag = datas[dataHash][msg.sender].tags[j];
datas[dataHash][msg.sender].tags[j] = tagHash;
break;
}
}

if (oldTag == bytes32(0)) {
datas[dataHash][msg.sender].tags.push(tagHash);
}

MetaData storage dataTagMeta = datas[dataHash][msg.sender].tag_info[tagHash];

if (!dataTagMeta.valid) {
datas[dataHash][msg.sender].tags.push(tagHash);
dataTagMeta.valid = true;
dataTagMeta.meta = data_tag_metas[i];
emit ReplaceDataTag(dataHash, msg.sender, bytes32(0), tagHash);
_rateDataTag(dataHash, msg.sender, tagHash, 1);
}

_rateDataTag(dataHash, msg.sender, tagHash, 1);
dataTagMeta.meta = data_tag_metas[i];

emit ReplaceDataTag(dataHash, msg.sender, oldTag, tagHash);
}
}

Expand Down
28 changes: 19 additions & 9 deletions test/test_data_tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ describe("data tag", function () {
tag_hashs["B"] = await data_tag.calcTagHash(["A", "B"]);
tag_hashs["C"] = await data_tag.calcTagHash(["A", "B", "C"]);
tag_hashs["D"] = await data_tag.calcTagHash(["A", "D"]);
console.log(`tag hash /A: ${tag_hashs["A"]}`);
console.log(`tag hash /A/B: ${tag_hashs["B"]}`);
console.log(`tag hash /A/B/C: ${tag_hashs["C"]}`);
console.log(`tag hash /A/D: ${tag_hashs["D"]}`);

signers = await ethers.getSigners();
})

it("set tag meta", async () => {
// 测试错误的空路径
await expect(data_tag.setTagMeta(["A", ""], "meta")).to.be.revertedWith("empty name");
// 测试错误的tag名称
await expect(data_tag.setTagMeta(["A", ""], "meta")).to.be.revertedWith("invalid name");
await expect(data_tag.setTagMeta(["A", "B/C"], "meta")).to.be.revertedWith("invalid name");

// 创建路径/A/B/C
let tx = data_tag.setTagMeta(["A", "B", "C"], "meta_A_B_C");
Expand Down Expand Up @@ -110,23 +115,28 @@ describe("data tag", function () {
})

it("add data tag", async () => {
await expect(data_tag.addDataTag(DATA_HASH, [await data_tag.calcTagHash(["A", "B", "E"])], ["signer0 add tag E"])).to.be.revertedWith("tag not exist");
// signer0给data添加tag D
let tx = data_tag.addDataTag(DATA_HASH, [tag_hashs["D"]], ["signer0 add tag D"]);
await expect(tx).to.emit(data_tag, "ReplaceDataTag").withArgs(DATA_HASH, signers[0].address, ethers.ZeroHash, tag_hashs["D"]);
await expect(tx).to.emit(data_tag, "RateDataTag").withArgs(DATA_HASH, signers[0].address, tag_hashs["D"], signers[0].address, 1);

// signer1给data添加tag C
tx = data_tag.connect(signers[1]).addDataTag(DATA_HASH, [tag_hashs["C"]], ["signer1 add tag C"]);
await expect(tx).to.emit(data_tag, "ReplaceDataTag").withArgs(DATA_HASH, signers[1].address, ethers.ZeroHash, tag_hashs["C"]);
await expect(tx).to.emit(data_tag, "RateDataTag").withArgs(DATA_HASH, signers[1].address, tag_hashs["C"], signers[1].address, 1);
// signer1给data添加tag B
tx = data_tag.connect(signers[1]).addDataTag(DATA_HASH, [tag_hashs["B"]], ["signer1 add tag B"]);
await expect(tx).to.emit(data_tag, "ReplaceDataTag").withArgs(DATA_HASH, signers[1].address, ethers.ZeroHash, tag_hashs["B"]);
await expect(tx).to.emit(data_tag, "RateDataTag").withArgs(DATA_HASH, signers[1].address, tag_hashs["B"], signers[1].address, 1);

// signer1给data添加tag A,应该失败
await expect(data_tag.connect(signers[1]).addDataTag(DATA_HASH, [tag_hashs["A"]], ["signer1 add tag A"])).to.be.revertedWith("child tag exist");

// signer1再给data添加tag D
await (await data_tag.connect(signers[1]).addDataTag(DATA_HASH, [tag_hashs["D"]], ["signer1 add tag C"])).wait();
// signer1再给data添加tag C,应该成功
tx = data_tag.connect(signers[1]).addDataTag(DATA_HASH, [tag_hashs["C"]], ["signer1 add tag C"]);
await expect(tx).to.emit(data_tag, "ReplaceDataTag").withArgs(DATA_HASH, signers[1].address, tag_hashs["B"], tag_hashs["C"]);
})

it("check data tag", async () => {
expect(await data_tag.getDataTags(DATA_HASH, signers[0].address)).to.deep.equal([tag_hashs["D"]]);
expect(await data_tag.getDataTags(DATA_HASH, signers[1].address)).to.deep.equal([tag_hashs["C"], tag_hashs["D"]]);
expect(await data_tag.getDataTags(DATA_HASH, signers[1].address)).to.deep.equal([tag_hashs["C"]]);

expect(await data_tag.connect(signers[1]).getDataTagMeta(DATA_HASH, signers[1].address, tag_hashs["C"])).to.deep.equal(["signer1 add tag C", 1, 0, 1]);
expect(await data_tag.getDataTagMeta(DATA_HASH, signers[0].address, tag_hashs["D"])).to.deep.equal(["signer0 add tag D", 1, 0, 1]);
Expand Down

0 comments on commit a0596bd

Please sign in to comment.