Skip to content

Commit

Permalink
update user-story, review code
Browse files Browse the repository at this point in the history
  • Loading branch information
waterflier committed Dec 23, 2023
1 parent 90aedf3 commit cadfbb4
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 50 deletions.
99 changes: 61 additions & 38 deletions contracts/public_data_storage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@ import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

using SortedScoreList for SortedScoreList.List;

//Review:这个作为ERC的一部分,要仔细考虑一下
interface IERCPublicDataContract {
//return the owner of the data
function getDataOwner(bytes32 dataHash) external view returns (address);

//return token data hash
function tokenDataHash(uint256 _tokenId) external view returns (bytes32);
}
// Review: 考虑有一些链的出块时间不确定,使用区块间隔要谨慎,可以用区块的时间戳

/**
* 有关奖励逻辑:
* 每个周期的奖励 = 上个周期的奖励 * 0.2 + 这个周期的所有赞助 * 0.2
* 因此,在每次收入奖励时,更新本周期的奖励额度
* 当本周期奖励额度为0时,以上个周期的奖励*0.2起始
* 可能有精度损失?
*
*
*/

/**
Expand All @@ -29,7 +33,7 @@ interface IERCPublicDataContract {

contract PublicDataStorage {
struct PublicData {
bytes32 mixedHash;
//bytes32 mixedHash;
address owner;
address sponsor;
address nftContract;
Expand All @@ -47,7 +51,7 @@ contract PublicDataStorage {
mapping(address => SupplierInfo) supplier_pledge;

mapping(bytes32 => PublicData) public_datas;
uint256 system_reward_pool;

mapping(bytes32 => uint256) data_balance;
// 这里相当于记得是supplier的show记录,共挑战用
struct ShowData {
Expand Down Expand Up @@ -75,8 +79,11 @@ contract PublicDataStorage {
mapping(uint256 => CycleInfo) cycle_infos;

uint256 startBlock;
uint256 sysBalance = 0;

// 合约常量参数
uint256 sysMinDepositRatio = 64;
uint256 sysMinPublicDataStorageWeeks = 96;
uint256 constant public blocksPerCycle = 17280;
uint256 constant public topRewards = 32;
uint256 constant public lockAfterShow = 240; // 成功的SHOW一小时内不允许提现
Expand All @@ -90,7 +97,7 @@ contract PublicDataStorage {
event PublicDataCreated(bytes32 mixedHash);
event SponserChanged(bytes32 mixedHash, address oldSponser, address newSponser);
event DataShowed(bytes32 mixedHash, address shower, uint256 score);
event Withdraw(bytes32 mixedHash, address user, uint256 amount);
event WithdrawAward(bytes32 mixedHash, address user, uint256 amount);
event ChallengeSuccess(address challenger, address challenged, uint256 show_block_number, bytes32 mixedHash, uint256 amount);

constructor(address _gwtToken) {
Expand Down Expand Up @@ -129,62 +136,70 @@ contract PublicDataStorage {
return cycleNumber;
}

//REVIEW:至在增加余额的时候更新周期会不会有潜在的bug? 比如存在周期空洞
function _addCycleReward(uint256 amount) private {
uint256 cycleNumber = _cycleNumber();
CycleInfo storage cycleInfo = cycle_infos[cycleNumber];
if (cycleInfo.total_award == 0) {
uint256 lastCycleReward = cycle_infos[cycleNumber - 1].total_award;
cycleInfo.total_award += lastCycleReward - (lastCycleReward * 4 / 5);
cycleInfo.total_award = (lastCycleReward * 3 / 20);
sysBalance += (lastCycleReward / 20);
cycle_infos[cycleNumber - 1].total_award = lastCycleReward * 4 / 5;
}
cycleInfo.total_award += amount;
}

// 计算这些空间对应多少GWT,单位是wei
//TODO:不满0.1G的,按0.1G计算
function _dataSizeToGWT(uint64 dataSize) internal pure returns(uint256) {
return (dataSize * 10 ** 18) >> 30;
}

function createPublicData(
bytes32 dataMixedHash,
uint64 depositRatio,
address publicDataContract,
uint256 depositAmount, //希望打入的GWT余额
address publicDataContract,//REVIEW:现在的实现简单,但应考虑更简单的导入整个NFT合约的所有Token的情况
uint256 tokenId
) public {
require(depositRatio >= sysMinDepositRatio, "deposit ratio is too small");
require(dataMixedHash != bytes32(0), "data hash is empty");

PublicData storage publicDataInfo = public_datas[dataMixedHash];
require(publicDataInfo.mixedHash == bytes32(0));
//TODO:这里不要再保存一次mixedHash了,贵
require(publicDataInfo.maxDeposit == 0);

publicDataInfo.mixedHash = dataMixedHash;
// get data size from data hash
uint64 dataSize = getDataSize(dataMixedHash);
//TODO: 要区分质押率和最小时长。最小时长是系统参数,质押率depositRatio是用户参数
//质押率影响用户SHOW数据所需要冻结的质押
//depositAmount = 数据大小*最小时长*质押率,
uint256 minAmount = depositRatio * _dataSizeToGWT(dataSize) * sysMinPublicDataStorageWeeks;
require(depositAmount > minAmount, "deposit amount is too small");
publicDataInfo.maxDeposit = depositAmount;
//publicDataInfo.mixedHash = dataMixedHash;
publicDataInfo.sponsor = msg.sender;
gwtToken.transferFrom(msg.sender, address(this), depositAmount);

if (publicDataContract == address(0)) {
publicDataInfo.owner = msg.sender;
} else if (tokenId == 0) {
// token id must be greater than 0
// 当合约不是IERCPublicDataContract时,是否可以将owner设置为contract地址?
// 是不是可以认为这是个Ownerable合约?
// TODO: 这里要考虑一下Owner的粒度: 合约Owner,Collection Owner,Token Owner
publicDataInfo.owner = Ownable(publicDataContract).owner();
} else {
require(dataMixedHash == IERCPublicDataContract(publicDataContract).tokenDataHash(tokenId));
publicDataInfo.nftContract = publicDataContract;
publicDataInfo.tokenId = tokenId;
}

// transfer deposit
require(depositRatio >= 48);

// get data size from data hash
uint64 dataSize = getDataSize(publicDataInfo.mixedHash);
uint256 depositAmount = depositRatio * _dataSizeToGWT(dataSize);

publicDataInfo.maxDeposit = depositAmount;

gwtToken.transferFrom(msg.sender, address(this), depositAmount);
data_balance[publicDataInfo.mixedHash] += (depositAmount * 8) / 10;
data_balance[dataMixedHash] += (depositAmount * 8) / 10;
uint256 system_reward = depositAmount - ((depositAmount * 8) / 10);
system_reward_pool += system_reward;

_addCycleReward(system_reward);

public_datas[dataMixedHash] = publicDataInfo;
//public_datas[dataMixedHash] = publicDataInfo;

emit PublicDataCreated(dataMixedHash);
emit SponserChanged(dataMixedHash, address(0), msg.sender);
Expand All @@ -201,33 +216,34 @@ contract PublicDataStorage {

function addDeposit(bytes32 dataMixedHash, uint256 depositAmount) public {
PublicData storage publicDataInfo = public_datas[dataMixedHash];
require(publicDataInfo.mixedHash != bytes32(0));
require(publicDataInfo.maxDeposit > 0);
require(publicDataInfo.owner == msg.sender);

// transfer deposit
gwtToken.transferFrom(msg.sender, address(this), depositAmount);
data_balance[publicDataInfo.mixedHash] += (depositAmount * 8) / 10;
//REVIEW:把balance放到publicDataInfo逻辑更单纯?
data_balance[dataMixedHash] += (depositAmount * 8) / 10;

uint256 system_reward = depositAmount - ((depositAmount * 8) / 10);
system_reward_pool += system_reward;


_addCycleReward(system_reward);

if (depositAmount > publicDataInfo.maxDeposit) {
publicDataInfo.maxDeposit = depositAmount;
}

if (depositAmount > ((publicDataInfo.maxDeposit * 11) / 10)) {
publicDataInfo.maxDeposit = depositAmount;
address oldSponser = publicDataInfo.sponsor;
publicDataInfo.sponsor = msg.sender;
emit SponserChanged(dataMixedHash, oldSponser, msg.sender);
if(oldSponser != msg.sender) {
publicDataInfo.sponsor = msg.sender;
emit SponserChanged(dataMixedHash, oldSponser, msg.sender);
}
}
}

function dataBalance(bytes32 dataMixedHash) public view returns(uint256) {
return data_balance[dataMixedHash];
}

function pledgeGWT(uint256 amount, bytes32 dataMixedHash) public {
function pledgeGwt(uint256 amount, bytes32 dataMixedHash) public {
gwtToken.transferFrom(msg.sender, address(this), amount);
supplier_pledge[msg.sender].pledge[dataMixedHash] += amount;

Expand All @@ -245,6 +261,7 @@ contract PublicDataStorage {
}

function _validPublicSupplier(address supplierAddress, bytes32 dataMixedHash) internal returns(bool) {
//TODO 这个质押保存的结构有点复杂了,可以简化
uint256 supplierPledge = supplier_pledge[supplierAddress].pledge[dataMixedHash];
uint256 showReward = data_balance[public_datas[dataMixedHash].mixedHash] / 10;
return supplierPledge > showDepositRatio * showReward;
Expand All @@ -269,11 +286,14 @@ contract PublicDataStorage {

// msg.sender is supplier
// show_hash = keccak256(abiEncode[sender, dataMixedHash, prev_block_hash, block_number])

function showData(bytes32 dataMixedHash, uint256 nonce_block, uint32 index, bytes32[] calldata m_path, bytes calldata leafdata, bytes32 noise) public {
address supplier = msg.sender;
require(nonce_block < block.number && block.number - nonce_block < maxNonceBlockDistance);
require(_validPublicSupplier(supplier, dataMixedHash));
// 每个块的每个supplier只能show一次数据

// 每个块的每个supplier只能show一次数据
// TODO:这里用this_block_show就好了,不用全保存下来
require(all_shows[block.number][supplier] == false);

// check block.number meets certain conditions
Expand All @@ -289,20 +309,21 @@ contract PublicDataStorage {

CycleInfo storage cycleInfo = cycle_infos[_cycleNumber()];
CycleDataInfo storage dataInfo = cycleInfo.data_infos[dataMixedHash];
dataInfo.score += getDataSize(publicDataInfo.mixedHash);
dataInfo.score += getDataSize(dataMixedHash);

// insert supplier into last_showers
if (dataInfo.shower_index >= 5) {
dataInfo.shower_index = 0;
}
dataInfo.last_showers[dataInfo.shower_index] = supplier;
dataInfo.shower_index += 1;


//TODO:计算奖励前先判断用户是否有足够的非冻结抵押余额,Show完后会更新LastShowTime,以及冻结的质押余额
// 给成功的show一些奖励
uint256 reward = data_balance[publicDataInfo.mixedHash] / 10;
uint256 reward = data_balance[dataMixedHash] / 10;
if (reward > 0) {
gwtToken.transfer(supplier, reward);
data_balance[publicDataInfo.mixedHash] -= reward;
data_balance[dataMixedHash] -= reward;
supplier_pledge[supplier].lastShowBlock = block.number;
}

Expand Down Expand Up @@ -386,15 +407,17 @@ contract PublicDataStorage {
}
}

function withdraw(uint cycleNumber, bytes32 dataMixedHash) public {
function withdrawAward(uint cycleNumber, bytes32 dataMixedHash) public {
// 判断这次的cycle已经结束
require(block.number > cycleNumber * blocksPerCycle + startBlock);
CycleInfo storage cycleInfo = cycle_infos[_cycleNumber()];
CycleDataInfo storage dataInfo = cycleInfo.data_infos[dataMixedHash];
//REVIEW:一次排序并保存的GAS和32次内存排序的成本问题?
uint256 scoreListRanking = cycleInfo.score_list.getRanking(dataMixedHash);
require(scoreListRanking > 0);

// 看看是谁来取
// REVIEW 这个函数做的事情比较多,建议拆分,或则命名更优雅一些
uint8 withdrawUser = _getWithdrawUser(dataMixedHash);

require(withdrawUser > 0);
Expand All @@ -412,6 +435,6 @@ contract PublicDataStorage {
dataInfo.withdraw_status |= withdrawUser;


emit Withdraw(dataMixedHash, msg.sender, reward);
emit WithdrawAward(dataMixedHash, msg.sender, reward);
}
}
27 changes: 15 additions & 12 deletions doc/erc/ideas.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@

## 可验证Hash格式
目标:存储证明里大量的数据只需要保存在
目标:存储证明里大量的数据只需要存储一次,然后通过存储证明来验证数据的存在

节点hash的大小是16byte (128bits), 1024/16*2 = 32, 2^32*1K = 4T

节点hash的大小是16byte, 1024/16*2 = 32, 2^32*1K = 4T

针对特定PH的存储证明(最短)
1.块高度,说明是基于哪个快得到的nonce和pos
Expand All @@ -38,27 +39,27 @@

## 私有数据存储证明
0. 用户(User)持有待保存的原始私有数据D
1. User决定把数据保存到供应商A,为A准备一个一次性的秘钥K,D通过K加密后得到K'

2. User认为供应商A丢失了数据,在链上提出挑战(一个Hash值) 32bytes
3. 供应商如果没有丢失数据,可以在Calldata里包含leaf_data。挑战结束。供应商获胜。
1. User决定把数据保存到供应商A,为A准备一个一次性的秘钥K,D通过K加密后得到D'。User将D'保存到A那,然后本地保留基于原始数据构造的挑战本和K
2. User认为供应商A丢失了D'(通常是通过链下判断),基于自己的挑战本在链上提出挑战:(一个32bytes Hash值)
3. 供应商如果没有丢失数据,可以在Calldata里包含leaf_data (1KB)。挑战结束。供应商获胜。
4. 如果供应商认为Hash并不包含在D'中,提出挑战非法 1byte
5. 用户通过Call Data中的index,默克尔路径来证明挑战合法,用户获胜。
5. 用户通过Call Data中的(index 4byte,默克尔路径,1KB )来证明挑战合法,用户获胜。

新方案
新方案可行么?
用户->nonce
供应商->m ---> timeout,supplier_win
用户->new_m,path_new_m,leaf_data_new_m ---> user_win
用户->path_m,leaf_data_m,new_m,path_new_m,leaf_data_new_m ---> user_win


## 公共数据存储证明

0. 能提交存储证明获得奖励的用户被称作Supplier,Supplier需要准备一定的质押币。
1. 区块高度为n的区块Hash得到 32bytes的nonce值和 32-992 的插入位置Pos
1. 区块高度为h的区块Hash得到 32bytes的nonce值和 32-992 的插入位置Pos
2. 为了生成正确的存储证明,Supplier遍历所有的叶子节点,在该位置插入nonce值,选择最合适的叶子节点m。让插入后的根Hash最小
3. Supplier在插入位置之前再计算一个32bytes的noise值,使得新的LeafData可以让默克尔树根Hash符合一个难度条件(比如最低位多少是0).对于同时进块的存储证明,难度高者胜出并得到奖励。
4. Supplier把存储证明{m,path,leaf_data,noise}提交到链上,即为一个有效的存储证明。可以拿到奖励.不需要PoW的场景可以进一步简化到 {h,m}
5. 链无法验证m是否正确,但其它拥有全量数据的Miner,如果发现m是伪造的,可以提交真实的{m,path_m,leaf_m} 来对已上连的存储证明进行挑战并在成功后赢得Supplier的质押币。
4. Supplier把存储证明{m,path,leaf_data,noise}提交到链上,即为一个有效的存储证明。可以拿到奖励.不需要PoW的场景可以进一步简化到 {h,m,path_m,m_leaf_data}
5. 链无法验证m是否正确,但其它拥有全量数据的Miner,如果发现m是伪造的,可以提交真实的{new_m,new_path_m,new_m_leaf_data} 来对已上连的存储证明进行挑战并在成功后赢得Supplier的质押币。
6. 上述设计也可改成Supplier只提交m,挑战者提供path_m, m_leaf_data,但这会导致挑战者需要多1倍的手续费。如果获得的质奖励太少,那么挑战者可能不会提交挑战。

## 为什么私有数据和公有数据的存储证明不同?

Expand All @@ -70,6 +71,8 @@
不解决数据是否是公共的问题,也不解决数据是否被访问的问题。该证明的存在只是说明该数据的副本是存在的。



## 已知问题
最小文件大小问题:基于上述逻辑不适合保存太小的文件

这种Hash结构的文件拼接问题?文件A,文件B巧妙的构成文件C,然后利用文件A和文件B的存储证明就可以构造文件C的存储证明
Loading

0 comments on commit cadfbb4

Please sign in to comment.