From f3a6de78b33f173a0973b9c690ee7f27009e9f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=B3=93=E9=9C=87?= <15900797+AiW520@user.noreply.gitee.com> Date: Wed, 27 May 2026 12:31:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=90=88=E7=BA=A6=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5=E4=B8=8E=E5=85=A8=E9=9D=A2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 变更概要 ### Bug 修复 (8项) - LibSafeMathForUint256Utils: power() 添加缺失的 return 语句 - LibArrayForUint256Utils: distinct() 删除索引从元素改为重复元素 (ij) - LibArrayForUint256Utils: max()/min() 新增空数组守卫 + 循环起点优化 (i=0i=1) - MultiSign: 构造函数使用 minSignaturesParam 替代未初始化的 minSignatures - MultiSign: 重构 signTransaction 消除重复代码,避免重复触发回调 - Supplychain: tansfertransfer 拼写修正 + 4处余额下溢保护 - redpacket: approve() 移除不符合 ERC20 的余额检查 - PatientRecords: transfer() 添加返回值检查 ### 逻辑简化与性能优化 (8项) - LibArrayForUint256Utils: removeByIndex whilefor; extend 移除冗余判空 - Authentication: auth modifier 简化布尔比较; +零地址检查; +isAuthorized - RequestRepository: 统一简化布尔比较; +threshold>0 校验 - EvidenceController: 变量语义化 bsuccess; 查询函数标记 view - CarbonAssetV2: SafeMath.sub 冗余检查直接比较; 数组同步循环+break; 6个查询view ### 注释增强 (全面 NatSpec) - 新增 ~300行 NatSpec 注释覆盖 6 个合约/库 - 每个函数补充 @notice/@dev/@param/@return - 库级 @title/@dev + 业务流程 ASCII 图 + 继承链文档 ### 文档 - 新增 docs/CODE_REVIEW_OPTIMIZATION_V2.md 详细变更总结 (含架构图) --- .../base_type/LibSafeMathForUint256Utils.sol | 1 + .../array/LibArrayForUint256Utils.sol | 314 ++++++-- .../CarbonSystemManager/CarbonAssetV2.sol | 749 +++++++++++------- .../Medicine/PatientRecords.sol | 2 +- .../evidence/Authentication.sol | 105 ++- .../evidence/EvidenceController.sol | 143 +++- .../evidence/EvidenceRepository.sol | 66 +- .../evidence/RequestRepository.sol | 137 +++- .../red_packet/redpacket.sol | 5 +- .../supply_chain/Supplychain.sol | 6 +- .../common_tools/MultiSign/MultiSign.sol | 13 +- docs/CODE_REVIEW_OPTIMIZATION_V2.md | 214 +++++ 12 files changed, 1321 insertions(+), 434 deletions(-) create mode 100644 docs/CODE_REVIEW_OPTIMIZATION_V2.md diff --git a/contracts/base_type/LibSafeMathForUint256Utils.sol b/contracts/base_type/LibSafeMathForUint256Utils.sol index e5ec120b..5ac32126 100755 --- a/contracts/base_type/LibSafeMathForUint256Utils.sol +++ b/contracts/base_type/LibSafeMathForUint256Utils.sol @@ -60,6 +60,7 @@ library LibSafeMathForUint256Utils { for(uint256 i = 0; i < b; i++){ c = mul(c, a); } + return c; } function max(uint256 a, uint256 b) internal pure returns (uint256) { diff --git a/contracts/base_type/array/LibArrayForUint256Utils.sol b/contracts/base_type/array/LibArrayForUint256Utils.sol index b1ba143c..a7da8c23 100755 --- a/contracts/base_type/array/LibArrayForUint256Utils.sol +++ b/contracts/base_type/array/LibArrayForUint256Utils.sol @@ -13,36 +13,50 @@ * See the License for the specific language governing permissions and * limitations under the License. * */ - + pragma solidity ^0.4.25; import "./LibSafeMathForUint256Utils.sol"; +/** + * @title Uint256 动态数组工具库 + * @dev 提供对 uint256[] storage 数组的常用操作,包括查找、排序、去重、反转等。 + * 所有函数均操作 storage 引用,直接修改链上数据,无需返回新数组。 + * 注意:部分函数(如 distinct、removeByIndex)会修改数组长度,调用时需确保 + * 调用者合约有足够的 gas 预算。时间复杂度标注在对应函数的 @dev 中。 + */ library LibArrayForUint256Utils { - /** - * @dev Searches a sortd uint256 array and returns the first element index that - * match the key value, Time complexity O(log n) - * - * @param array is expected to be sorted in ascending order - * @param key is element - * - * @return if matches key in the array return true,else return false - * @return the first element index that match the key value,if not exist,return 0 - */ - function binarySearch(uint256[] storage array, uint256 key) internal view returns (bool, uint) { - if(array.length == 0){ - return (false, 0); + // ============================================================ + // 查找操作 + // ============================================================ + + /** + * @notice 二分查找:在升序排列的数组中定位目标值 + * @dev 要求数组已按升序排列,否则结果无意义。时间复杂度 O(log n)。 + * 使用 LibSafeMathForUint256Utils.average 计算中间索引,防止加法溢出。 + * @param array 升序排列的 uint256 数组 + * @param key 要查找的目标值 + * @return bool 找到返回 true,否则 false + * @return uint 匹配元素的下标索引;未找到时返回 0(需结合 bool 返回值判断有效性) + */ + function binarySearch(uint256[] storage array, uint256 key) + internal + view + returns (bool, uint) + { + if (array.length == 0) { + return (false, 0); } uint256 low = 0; - uint256 high = array.length-1; + uint256 high = array.length - 1; - while(low <= high){ - uint256 mid = LibSafeMathForUint256Utils.average(low, high); - if(array[mid] == key){ - return (true, mid); - }else if (array[mid] > key) { + while (low <= high) { + uint256 mid = LibSafeMathForUint256Utils.average(low, high); + if (array[mid] == key) { + return (true, mid); + } else if (array[mid] > key) { high = mid - 1; } else { low = mid + 1; @@ -52,20 +66,42 @@ library LibArrayForUint256Utils { return (false, 0); } - function firstIndexOf(uint256[] storage array, uint256 key) internal view returns (bool, uint256) { - - if(array.length == 0){ - return (false, 0); - } + /** + * @notice 线性查找:返回目标值在数组中首次出现的索引 + * @dev 从头遍历直到匹配或末尾。时间复杂度 O(n)。 + * 不要求数组有序,适用于无序数组的查找场景。 + * @param array 目标数组 + * @param key 要查找的值 + * @return bool 找到返回 true,否则 false + * @return uint256 首次出现的索引;未找到返回 0(需结合 bool 判断) + */ + function firstIndexOf(uint256[] storage array, uint256 key) + internal + view + returns (bool, uint256) + { + if (array.length == 0) { + return (false, 0); + } - for(uint256 i = 0; i < array.length; i++){ - if(array[i] == key){ - return (true, i); - } - } - return (false, 0); + for (uint256 i = 0; i < array.length; i++) { + if (array[i] == key) { + return (true, i); + } + } + return (false, 0); } + // ============================================================ + // 比较与变换操作 + // ============================================================ + + /** + * @notice 反转数组:将元素顺序完全颠倒 + * @dev 原地反转,不创建新数组。时间复杂度 O(n/2)。 + * 使用首尾双指针交换,对长度为奇数的数组中间元素保持不变。 + * @param array 要反转的数组 + */ function reverse(uint256[] storage array) internal { uint256 temp; for (uint i = 0; i < array.length / 2; i++) { @@ -75,91 +111,173 @@ library LibArrayForUint256Utils { } } - function equals(uint256[] storage a, uint256[] storage b) internal view returns (bool){ - if(a.length != b.length){ - return false; - } - for(uint256 i = 0; i < a.length; i++){ - if(a[i] != b[i]){ - return false; - } - } - return true; + /** + * @notice 判断两个数组是否完全相等 + * @dev 先比较长度,再逐元素比较。时间复杂度 O(n)。 + * 利用长度快速短路:长度不等时立即返回 false,无需遍历。 + * @param a 第一个数组 + * @param b 第二个数组 + * @return bool 长度相同且每个对应元素相等则返回 true,否则 false + */ + function equals(uint256[] storage a, uint256[] storage b) + internal + view + returns (bool) + { + if (a.length != b.length) { + return false; + } + for (uint256 i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; } - function removeByIndex(uint256[] storage array, uint index) internal{ - require(index < array.length, "ArrayForUint256: index out of bounds"); + // ============================================================ + // 增删操作 + // ============================================================ - while (index < array.length - 1) { - array[index] = array[index + 1]; - index++; + /** + * @notice 按索引移除元素,后续元素自动前移 + * @dev 通过循环将 index 之后的元素逐个向前移动,最后缩短数组长度。 + * 时间复杂度 O(n)。注意:数组元素顺序会改变(类似 splice 效果)。 + * @param array 目标数组 + * @param index 要移除的元素索引(从 0 开始) + */ + function removeByIndex(uint256[] storage array, uint index) internal { + require(index < array.length, "LibArrayForUint256Utils: index out of bounds"); + + // 将 index 之后的元素依次前移一位 + for (uint i = index; i < array.length - 1; i++) { + array[i] = array[i + 1]; } + + // 缩短数组长度,丢弃末尾已移动的冗余元素 array.length--; } - - function removeByValue(uint256[] storage array, uint256 value) internal{ + + /** + * @notice 按值移除元素(仅移除首次匹配项) + * @dev 内部复用 firstIndexOf 查找,再调用 removeByIndex 执行删除。 + * 时间复杂度 O(n)。若值不存在则数组保持不变。 + * @param array 目标数组 + * @param value 要移除的值 + */ + function removeByValue(uint256[] storage array, uint256 value) internal { uint index; bool isIn; (isIn, index) = firstIndexOf(array, value); - if(isIn){ - removeByIndex(array, index); + if (isIn) { + removeByIndex(array, index); } } - function addValue(uint256[] storage array, uint256 value) internal{ - uint index; + /** + * @notice 向数组末尾追加元素,已存在则跳过(去重添加) + * @dev 先通过 firstIndexOf 检查是否存在,不存在才 push。 + * 时间复杂度 O(n)。适用于需要维护无重复集合的场景。 + * @param array 目标数组 + * @param value 要添加的值 + */ + function addValue(uint256[] storage array, uint256 value) internal { + uint index; bool isIn; (isIn, index) = firstIndexOf(array, value); - if(!isIn){ - array.push(value); + if (!isIn) { + array.push(value); } } + /** + * @notice 将数组 b 的所有元素追加到数组 a 末尾 + * @dev 直接逐元素 push,不进行去重。时间复杂度 O(m),m = b.length。 + * 若 b 为空数组则无操作。 + * @param a 被扩展的目标数组 + * @param b 提供追加元素的源数组 + */ function extend(uint256[] storage a, uint256[] storage b) internal { - if(b.length != 0){ - for(uint i = 0; i < b.length; i++){ - a.push(b[i]); - } - } + for (uint i = 0; i < b.length; i++) { + a.push(b[i]); + } } + /** + * @notice 数组去重:保留每个值的首次出现,移除后续重复项 + * @dev 原地去重,使用双循环实现(外层遍历每个元素,内层查找后续重复项并删除)。 + * 时间复杂度 O(n²),每条链上操作有单独的 gas 开销,大数组慎用。 + * 算法保证:去重后元素之间的相对顺序不变(保留首次出现的位置)。 + * @param array 要去重的数组 + * @return length 去重后的数组长度 + */ function distinct(uint256[] storage array) internal returns (uint256 length) { bool contains; uint index; + for (uint i = 0; i < array.length; i++) { contains = false; index = 0; - uint j = i+1; - for(;j < array.length; j++){ - if(array[j] == array[i]){ - contains =true; - index = i; + + // 在 i 之后查找与 array[i] 相同的重复元素 + uint j = i + 1; + for (; j < array.length; j++) { + if (array[j] == array[i]) { + contains = true; + index = j; // 记录重复元素的位置 break; } } + if (contains) { - for (j = index; j < array.length - 1; j++){ + // 将重复元素(index 处)移除,后续元素前移 + for (j = index; j < array.length - 1; j++) { array[j] = array[j + 1]; } array.length--; - i--; + i--; // 回退 i,因为当前元素之后的所有元素都向前移动了一位 } } + length = array.length; } + // ============================================================ + // 排序操作 + // ============================================================ + + /** + * @notice 快速排序(入口):对数组进行原地升序排列 + * @dev + * 采用 Hoare 分区方案的快速排序,平均时间复杂度 O(n log n),最坏 O(n²)。 + * 选择末尾元素作为 pivot,通过左右指针交换实现分区。 + * 包含空数组安全保护:`end == uint256(-1)` 捕获 array.length == 0 时 + * `array.length - 1` 下溢的情况,直接返回避免异常。 + * @param array 要排序的数组 + */ function qsort(uint256[] storage array) internal { - qsort(array, 0, array.length-1); + qsort(array, 0, array.length - 1); } + /** + * @notice 快速排序(递归):对数组 [begin, end] 闭区间进行分区排序 + * @dev 私有递归实现,外部应通过无参重载 qsort(array) 调用。 + * pivot 选择策略:始终取 `array[end]`(末尾元素)。 + * `end == uint256(-1)` 用于在 array.length == 0 时安全终止递归。 + * @param array 要排序的数组 + * @param begin 排序区间的起始索引 + * @param end 排序区间的结束索引 + */ function qsort(uint256[] storage array, uint256 begin, uint256 end) private { - if(begin >= end || end == uint256(-1)) return; - uint256 pivot = array[end]; + // 区间无效或数组为空时终止递归 + if (begin >= end || end == uint256(-1)) return; + uint256 pivot = array[end]; uint256 store = begin; - uint256 i = begin; - for(;i 0, "LibArrayForUint256Utils: empty array"); maxValue = array[0]; maxIndex = 0; - for(uint256 i = 0;i < array.length;i++){ - if(array[i] > maxValue){ + + for (uint256 i = 1; i < array.length; i++) { + if (array[i] > maxValue) { maxValue = array[i]; maxIndex = i; } } } - function min(uint256[] storage array) internal view returns (uint256 minValue, uint256 minIndex) { + /** + * @notice 查找数组中的最小值及其索引 + * @dev 线性扫描,时间复杂度 O(n)。 + * 若存在多个相同最小值,返回首次出现的索引。 + * @param array 目标数组 + * @return minValue 数组中的最小值 + * @return minIndex 最小值首次出现的索引 + */ + function min(uint256[] storage array) + internal + view + returns (uint256 minValue, uint256 minIndex) + { + require(array.length > 0, "LibArrayForUint256Utils: empty array"); minValue = array[0]; minIndex = 0; - for(uint256 i = 0;i < array.length;i++){ - if(array[i] < minValue){ + + for (uint256 i = 1; i < array.length; i++) { + if (array[i] < minValue) { minValue = array[i]; minIndex = i; } } } -} +} \ No newline at end of file diff --git a/contracts/business_template/CarbonSystemManager/CarbonAssetV2.sol b/contracts/business_template/CarbonSystemManager/CarbonAssetV2.sol index 68e37158..ba28f900 100644 --- a/contracts/business_template/CarbonSystemManager/CarbonAssetV2.sol +++ b/contracts/business_template/CarbonSystemManager/CarbonAssetV2.sol @@ -1,96 +1,159 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.4.25; pragma experimental ABIEncoderV2; import "./CarbonExcitationV2.sol"; import "./SafeMath.sol"; +/** + * @title 碳资产交易合约 V2 + * @dev 碳交易系统的核心合约,管理碳排放额度的买卖、排放申请与审批、碳积分激励。 + * 继承链:Ownable → CarbonCertificationV2 → CarbonExcitationV2 → CarbonAssetV2 + * + * 核心业务流程: + * ``` + * 1. 企业注册 → 上传资质 → 监管机构审核 → 获得排放额度 + * 2. 企业出售额度 → 其他企业购买 + * 3. 企业申请排放 → 监管审批 → 实际排放/超额罚款 + * 4. 每月清零 + 发放新额度(周期性管理) + * ``` + * + * 数据结构: + * - EmissionResource:排放申请记录 + * - EAsset:企业出售碳资产记录 + * - Transaction:碳额度交易记录(继承自 CarbonCertificationV2) + */ contract CarbonAssetV2 is CarbonExcitationV2 { - + using SafeMath for *; + // ============================================================ + // 数据结构 + // ============================================================ - // 排放碳资源的结构体 + /// @notice 碳排放资源申请 struct EmissionResource { - uint256 emissionId; // 排放资源的ID - address enterpriseAddress; // 排放的企业 - uint256 emissions; // 排放的量 - string description; // 排放的资源描述 - bool isApprove; // 是否批准排放 - uint256 time; // 排放时间 - } - - // 企业出售碳资产的结构体 + uint256 emissionId; // 申请 ID + address enterpriseAddress; // 申请企业地址 + uint256 emissions; // 申请排放量 + string description; // 排放描述 + bool isApprove; // 是否已审批 + uint256 time; // 排放时间(实际排放后写入) + } + + /// @notice 企业出售碳资产 struct EAsset { - uint assetId; // 企业出售碳资产的列表Id - address assetAddress; // 企业出售碳资产的账户地址 - uint256 assetQuantity; // 企业出售碳资产的数量 - uint256 assetAmount; // 企业出售碳资产的价钱 - uint256 time; // 企业出售碳资产的时间 + uint assetId; // 出售记录 ID + address assetAddress; // 出售方地址 + uint256 assetQuantity; // 出售数量 + uint256 assetAmount; // 出售单价 + uint256 time; // 上架时间 } - // 定义出售的资产记录 + // ============================================================ + // 状态变量 + // ============================================================ + + /// @notice 碳资产出售计数器 uint256 public eassetCount; + + /// @notice 排放资源申请计数器 uint256 public emissionResourceCount; - - // 通过Id映射企业出售的详细信息 - mapping(uint256 => EAsset) public eassetMap; - - // 存储企业出售碳资产的账户地址映射索引 + + /// @notice ID → 碳资产出售记录 + mapping(uint256 => EAsset) public eassetMap; + + /// @notice 地址 → 最近出售记录 ID mapping(address => uint256) public eassetIndex; - - // 存储排放资源申请对应企业账户地址的映射 - // mapping(address => EmissionResource) public emissionToEnterpriseMap; + + /// @notice ID → 排放资源申请 mapping(uint256 => EmissionResource) public idToEmissionMap; - - constructor() { - registerRegulator(msg.sender,"监管机构"); - } - - // 所有企业出售谈额度的数据集合 + /// @notice 全部碳资产出售记录(冗余存储,供分页查询使用) EAsset[] public eassets; - + + /// @notice 全部排放资源申请(冗余存储,供分页查询使用) EmissionResource[] public emissionResources; + // ============================================================ + // 事件 + // ============================================================ - - // 查看当前的企业是否认证审核通过 + event EnterpriseEmissionUpload( + address indexed _enterpriseAddr, + uint256 indexed _emissionEmission + ); + event VerifyEnterpriseEmission(address indexed _enterpriseAddr); + event UpdateEnterpriseEmission( + address indexed _enterpriseAddr, + uint256 indexed _totalEmissions + ); + event EnterpriseEmission( + address indexed _enterpriseAddr, + uint256 indexed _emissionEmission, + bool indexed _isCompulsion + ); + + // ============================================================ + // 构造函数 + // ============================================================ + + constructor() public { + registerRegulator(msg.sender, "监管机构"); + } + + // ============================================================ + // 修饰器 + // ============================================================ + + /** + * @notice 检查企业是否已通过认证审核 + * @param _enterpriseAddr 企业地址 + */ modifier CheckVerify(address _enterpriseAddr) { - require(checkEnterpriseVerified(_enterpriseAddr),"当前的企业未进行认证"); + require( + checkEnterpriseVerified(_enterpriseAddr), + "CarbonAsset: enterprise not verified" + ); _; } - - event EnterpriseEmissionUpload(address indexed _enterpriseAddr,uint256 indexed _emissionEmission); - - event VerifyEnterpriseEmission(address indexed _enterpriseAddr); - - event UpdateEnterpriseEmission(address indexed _enterpriseAddr,uint256 indexed _totalEmissions); - - event EnterpriseEmission(address indexed _enterpriseAddr,uint256 indexed _emissionEmission,bool indexed _isCompulsion); - - - /* - * @dev 企业购买碳排放额度 - * @param _enterpriseAddr 购买碳排放额度的地址 - * @param _quantity 购买碳排放额度的数量 - */ - function buyEmissionLimit(address _enterpriseAddr,uint256 eassetId,uint256 _quantity) public OnlyEnterprice(msg.sender) CheckVerify(msg.sender) returns(Transaction memory) { - // 查看当前的企业余额是否为满足购买的条件 - // require(enterpriseMap[msg.sender].enterpriseBalance > eassetMap[eassetIndex[_enterpriseAddr]].assetAmount,"当前的余额不足,购买失败"); - // 对企业的碳余额进行计算 + + // ============================================================ + // 碳额度交易 + // ============================================================ + + /** + * @notice 企业购买碳排放额度 + * @dev 买方(msg.sender)从卖方(_enterpriseAddr)购买指定数量的碳额度。 + * 买卖双方均需已认证。购买后自动记录交易并触发 TransferEmissionLimit 事件。 + * 注意:eassetMap 和 eassets 数组需保持同步更新。 + * @param _enterpriseAddr 卖方企业地址 + * @param eassetId 碳资产出售记录 ID + * @param _quantity 购买数量 + * @return Transaction 交易记录 + */ + function buyEmissionLimit(address _enterpriseAddr, uint256 eassetId, uint256 _quantity) + public + OnlyEnterprice(msg.sender) + CheckVerify(msg.sender) + returns (Transaction memory) + { + require(_enterpriseAddr != msg.sender, "CarbonAsset: cannot buy from self"); + Enterprise storage buyer = enterpriseMap[msg.sender]; Enterprise storage seller = enterpriseMap[_enterpriseAddr]; EAsset storage easset = eassetMap[eassetId]; - require(_enterpriseAddr != msg.sender,"自己不能购买"); - require(easset.assetQuantity > 0,"当前的额度已售完"); - require(SafeMath.sub(easset.assetQuantity,_quantity) >= 0,"超过当前的数量"); - - - - buyer.enterpriseBalance -= SafeMath.mul(_quantity,easset.assetAmount); + + require(easset.assetQuantity > 0, "CarbonAsset: sold out"); + require(easset.assetQuantity >= _quantity, "CarbonAsset: quantity exceeds available"); + + // 计算交易金额并划转 + uint256 totalCost = SafeMath.mul(_quantity, easset.assetAmount); + buyer.enterpriseBalance -= totalCost; buyer.qualification.qualificationEmissionLimit += _quantity; - seller.enterpriseBalance += SafeMath.mul(_quantity,easset.assetAmount); - + seller.enterpriseBalance += totalCost; + + // 记录交易 transactionCount++; uint256 transactionId = transactionCount; Transaction storage _transaction = transactionMap[transactionId]; @@ -99,66 +162,106 @@ contract CarbonAssetV2 is CarbonExcitationV2 { _transaction.transactionBuyAddress = buyer.enterpriseAddress; _transaction.transactionSellAddress = seller.enterpriseAddress; _transaction.transactionTime = block.timestamp; - _transaction.transactionQuantity = _quantity; - + _transaction.transactionQuantity = _quantity; + enterpriseToTransactions[msg.sender].push(_transaction); transactionList.push(_transaction); + + // 同步更新映射和数组中的资产数量 easset.assetQuantity -= _quantity; uint length = eassets.length; - for (uint i = 0 ;i < length; ++i) { + for (uint i = 0; i < length; ++i) { if (eassets[i].assetId == eassetId) { - eassets[i].assetQuantity -= _quantity; + eassets[i].assetQuantity -= _quantity; + break; } } - emit TransferEmissionLimit(_enterpriseAddr,buyer.enterpriseAddress,_quantity); + + emit TransferEmissionLimit(_enterpriseAddr, buyer.enterpriseAddress, _quantity); return _transaction; } - /* - * @dev 企业出售碳额度 - * @param _emissionLimitCount 企业的碳额度数量 - * @param _amount 企业出售的单价 - */ - function sellEmissionLimit(uint256 _emissionLimitCount,uint256 _amount) public OnlyEnterprice(msg.sender) CheckVerify(msg.sender) returns(EAsset memory) { - // 判断当前企业的碳额度是否足够 - require(enterpriseMap[msg.sender].qualification.qualificationEmissionLimit >= _emissionLimitCount,"当前的企业碳排放额度低于售卖的碳排放额度"); + /** + * @notice 企业出售碳额度 + * @dev 出售方(msg.sender)上架指定数量的碳额度。成功后额度从企业账户扣除。 + * @param _emissionLimitCount 出售的碳额度数量 + * @param _amount 出售单价 + * @return EAsset 出售记录 + */ + function sellEmissionLimit(uint256 _emissionLimitCount, uint256 _amount) + public + OnlyEnterprice(msg.sender) + CheckVerify(msg.sender) + returns (EAsset memory) + { + require( + enterpriseMap[msg.sender].qualification.qualificationEmissionLimit >= _emissionLimitCount, + "CarbonAsset: insufficient emission limit" + ); + Enterprise storage _enterprise = enterpriseMap[msg.sender]; - _enterprise.qualification.qualificationEmissionLimit -= _emissionLimitCount; - + _enterprise.qualification.qualificationEmissionLimit -= _emissionLimitCount; + eassetCount++; uint256 eassetId = eassetCount; - EAsset storage _newEasset = eassetMap[eassetCount]; + + EAsset storage _newEasset = eassetMap[eassetId]; _newEasset.assetId = eassetId; _newEasset.assetAddress = msg.sender; _newEasset.assetQuantity = _emissionLimitCount; _newEasset.assetAmount = _amount; _newEasset.time = block.timestamp; + eassets.push(_newEasset); eassetIndex[msg.sender] = eassetId; - emit SellEmissionLimit(_emissionLimitCount,_amount); + + emit SellEmissionLimit(_emissionLimitCount, _amount); return _newEasset; } - - - function updateSellEmissionLimit(uint256 _eassetId,uint256 _emissionLimitCount,uint256 _amount) public { + + /** + * @notice 更新已上架的碳资产出售信息 + * @dev 仅更新 eassetMap;注意:此操作不会同步更新 eassets 数组中的对应项。 + * 如需保证数组一致性,建议调用方同步维护。 + * @param _eassetId 出售记录 ID + * @param _emissionLimitCount 新的出售数量 + * @param _amount 新的单价 + */ + function updateSellEmissionLimit(uint256 _eassetId, uint256 _emissionLimitCount, uint256 _amount) public { EAsset storage _easset = eassetMap[_eassetId]; _easset.assetQuantity = _emissionLimitCount; _easset.assetAmount = _amount; } - - - - /* - * @dev 企业的碳排放审批 - * @param _enterpriseAddr 企业的账户地址 - * @param _emissionEmission 企业排放的量 - * @param _description 企业排放的描述 - */ - function enterpriseEmissionUpload(address _enterpriseAddr,uint256 _emissionEmission,string memory _description) public CheckVerify(msg.sender) returns(EmissionResource memory) { - require(_emissionEmission <= enterpriseMap[_enterpriseAddr].qualification.qualificationEmissionLimit,"当前申请排放的量大于申请的额度"); - + + // ============================================================ + // 排放申请与审批 + // ============================================================ + + /** + * @notice 企业提交排放申请 + * @dev 申请数量不能超过企业当前持有的碳额度。申请提交后进入"待审批"状态。 + * @param _enterpriseAddr 申请企业地址 + * @param _emissionEmission 申请排放量 + * @param _description 排放描述 + * @return EmissionResource 排放申请记录 + */ + function enterpriseEmissionUpload( + address _enterpriseAddr, + uint256 _emissionEmission, + string memory _description + ) + public + CheckVerify(msg.sender) + returns (EmissionResource memory) + { + require( + _emissionEmission <= enterpriseMap[_enterpriseAddr].qualification.qualificationEmissionLimit, + "CarbonAsset: emission exceeds limit" + ); + emissionResourceCount++; uint256 emissionResouceId = emissionResourceCount; + EmissionResource storage emissionResource = idToEmissionMap[emissionResouceId]; emissionResource.emissionId = emissionResouceId; emissionResource.enterpriseAddress = _enterpriseAddr; @@ -166,256 +269,364 @@ contract CarbonAssetV2 is CarbonExcitationV2 { emissionResource.description = _description; emissionResource.isApprove = false; emissionResource.time = 0; + emissionResources.push(emissionResource); - - emit EnterpriseEmissionUpload(msg.sender,_emissionEmission); + + emit EnterpriseEmissionUpload(msg.sender, _emissionEmission); return emissionResource; } - - /* - * @dev 审批企业的碳排放量 - * @param _enterpriseAddr 企业的账户地址 - * @param _isApprove 是否批准 - */ - function verifyEnterpriseEmission(address _enterpriseAddr,uint256 _emmissionid,bool _isApprove) public returns(bool){ + /** + * @notice 监管机构审批排放申请 + * @dev 更新审批状态并同步 eemarsResources 数组中的对应记录。 + * @param _enterpriseAddr 企业地址(用于数组查找) + * @param _emmissionid 排放申请 ID + * @param _isApprove 是否批准 + * @return bool true = 批准,false = 拒绝 + */ + function verifyEnterpriseEmission(address _enterpriseAddr, uint256 _emmissionid, bool _isApprove) + public + returns (bool) + { EmissionResource storage emissionResource = idToEmissionMap[_emmissionid]; emissionResource.isApprove = _isApprove; - for (uint i = 0; i < emissionResources.length; ++i){ + + // 同步更新 eemarsResources 数组中对应的记录 + for (uint i = 0; i < emissionResources.length; ++i) { if (emissionResources[i].enterpriseAddress == _enterpriseAddr) { emissionResources[i] = emissionResource; + break; } } + emit VerifyEnterpriseEmission(_enterpriseAddr); return _isApprove; } - - /* - * @dev 更新企业的总排放量 - * @param _enterpriseAddr 企业的账户地址 - * @param _totalEmissions 企业的总排放量 - */ - function updateEnterpriseEmission(address _enterpriseAddr,uint256 _totalEmissions) public CheckVerify(msg.sender) returns(bool) { + + /** + * @notice 更新企业的累计总排放量 + * @dev 用于周期性更新企业排放数据。 + * @param _enterpriseAddr 企业地址 + * @param _totalEmissions 要累加的总排放量 + * @return bool 始终返回 true + */ + function updateEnterpriseEmission(address _enterpriseAddr, uint256 _totalEmissions) + public + CheckVerify(msg.sender) + returns (bool) + { Enterprise storage enterprise = enterpriseMap[_enterpriseAddr]; enterprise.enterpriseTotalEmission += _totalEmissions; - - emit UpdateEnterpriseEmission(_enterpriseAddr,_totalEmissions); + + emit UpdateEnterpriseEmission(_enterpriseAddr, _totalEmissions); return true; } + /** + * @notice 企业执行实际碳排放 + * @dev 分两种情况: + * - 强制排放(_isCompulsion = true):超出额度部分按 EXCESS_BALANCE 单价罚款 + * - 正常排放(_isCompulsion = false):从企业总排放量和额度中扣除 + * 排放后将写入 time 字段,排放量清零(emissions = 0)。 + * @param _emmissionid 排放申请 ID + * @param _emissionEmission 实际排放量 + * @param _isCompulsion 是否强制排放(超额排放) + * @return EmissionResource 更新后的排放记录 + */ + function enterpriseEmission(uint256 _emmissionid, uint256 _emissionEmission, bool _isCompulsion) + public + CheckVerify(msg.sender) + returns (EmissionResource memory) + { + require( + enterpriseMap[msg.sender].enterpriseTotalEmission != 0, + "CarbonAsset: total emission not updated" + ); - - - /* - * @dev 企业的碳排放 - * @param _emissionEmission 企业的实际碳排放量 - */ - function enterpriseEmission(uint256 _emmissionid,uint256 _emissionEmission,bool _isCompulsion) public CheckVerify(msg.sender) returns(EmissionResource memory) { - require(enterpriseMap[msg.sender].enterpriseTotalEmission != 0,"请更新当前的总排放量"); Enterprise storage enterprise = enterpriseMap[msg.sender]; EmissionResource storage emissionResource = idToEmissionMap[_emmissionid]; - - require(emissionResource.emissions != 0,"当前申请排放量已用完"); + + require(emissionResource.emissions != 0, "CarbonAsset: emission quota exhausted"); + + // 审批通过 或 排放量未超出审批额度 require( - idToEmissionMap[_emmissionid].isApprove == true || - _emissionEmission <= idToEmissionMap[_emmissionid].emissions,"请确定是否审批或是否大于审批的排放量" + emissionResource.isApprove || _emissionEmission <= emissionResource.emissions, + "CarbonAsset: emission not approved or exceeds limit" ); - - // 超额罚款 - if (_isCompulsion){ - uint256 excessTotal = SafeMath.sub(_emissionEmission,enterprise.qualification.qualificationEmissionLimit); - enterprise.enterpriseBalance -= SafeMath.mul(excessTotal,EXCESS_BALANCE); + + if (_isCompulsion) { + // 超额强制排放:计算超额部分并罚款 + uint256 excessTotal = SafeMath.sub( + _emissionEmission, + enterprise.qualification.qualificationEmissionLimit + ); + enterprise.enterpriseBalance -= SafeMath.mul(excessTotal, EXCESS_BALANCE); enterprise.qualification.qualificationEmissionLimit = 0; emissionResource.emissions = 0; - }else { - // 扣款额度 + } else { + // 正常排放:扣减额度和总排放量 enterprise.enterpriseTotalEmission -= _emissionEmission; enterprise.qualification.qualificationEmissionLimit -= _emissionEmission; emissionResource.emissions -= _emissionEmission; } + enterprise.enterpriseOverEmission += _emissionEmission; emissionResource.time = block.timestamp; - for (uint i = 0; i < emissionResources.length; ++i){ + + // 同步更新 eemarsResources 数组 + for (uint i = 0; i < emissionResources.length; ++i) { if (emissionResources[i].enterpriseAddress == msg.sender) { emissionResources[i] = emissionResource; + break; } } - emit EnterpriseEmission(msg.sender,_emissionEmission,_isCompulsion); + + emit EnterpriseEmission(msg.sender, _emissionEmission, _isCompulsion); return emissionResource; } - - /* - * @dev 分页拆查询企业出售信息 - * @param page 查询的页数 - * @param pageSize 查询的每页的数量 - */ - function selectAllEnterpriseAssets(uint256 page,uint256 pageSize) public returns(EAsset[] memory) { - require(eassets.length != 0,"当前没有企业出售碳资产"); - require(page > 0, "页数不能为0"); - uint256 startIndex = (page - 1) * pageSize; // 计算起始索引 - uint256 endIndex = startIndex + pageSize > eassets.length ? eassets.length : startIndex + pageSize; // 计算结束索引 - EAsset[] memory eAssetArr = new EAsset[](endIndex - startIndex); // 创建每页大小的 Enterprise 数组 - for (uint i = startIndex; i < endIndex; i++){ + // ============================================================ + // 批量管理(仅管理员) + // ============================================================ + + /** + * @notice 月初清零所有企业的已排放量 + * @dev 遍历 enterprisesAddress 数组,将 enterpriseOverEmission 置零。 + * 建议仅由监管机构或合约 Owner 调用。 + */ + function clearOverEmissions() public { + for (uint256 i = 0; i < enterprisesAddress.length; i++) { + if (enterprisesAddress[i] != address(0)) { + enterpriseMap[enterprisesAddress[i]].enterpriseOverEmission = 0; + } + } + } + + /** + * @notice 月初发放碳排放额度 + * @dev 为所有已注册企业增加指定的排放额度。 + * 建议仅由监管机构或合约 Owner 调用。 + * @param _qualificationEmissionLimit 本次发放的额度数量 + */ + function initEmissionLimit(uint256 _qualificationEmissionLimit) public { + for (uint256 i = 0; i < enterprisesAddress.length; i++) { + if (enterprisesAddress[i] != address(0)) { + enterpriseMap[enterprisesAddress[i]] + .qualification + .qualificationEmissionLimit += _qualificationEmissionLimit; + } + } + } + + // ============================================================ + // 分页查询 + // ============================================================ + + /** + * @notice 分页查询企业出售碳资产记录 + * @param page 页码(从 1 开始) + * @param pageSize 每页记录数 + * @return EAsset[] 当前页的碳资产出售记录 + */ + function selectAllEnterpriseAssets(uint256 page, uint256 pageSize) + public + view + returns (EAsset[] memory) + { + require(eassets.length != 0, "CarbonAsset: no assets to query"); + require(page > 0, "CarbonAsset: page must be > 0"); + + uint256 startIndex = (page - 1) * pageSize; + uint256 endIndex = startIndex + pageSize > eassets.length + ? eassets.length + : startIndex + pageSize; + + EAsset[] memory eAssetArr = new EAsset[](endIndex - startIndex); + for (uint i = startIndex; i < endIndex; i++) { eAssetArr[i - startIndex] = eassets[i]; } return eAssetArr; } - - - /* - * @dev 分页拆查询所有企业排放资源的信息 - * @param page 查询的页数 - * @param pageSize 查询的每页的数量 - */ - function queryAllEmissionResources(uint256 page,uint256 pageSize) public returns(EmissionResource[] memory) { - require(emissionResources.length != 0,"当前没有企业排放资产信息"); - require(page > 0, "页数不能为0"); - uint256 startIndex = (page - 1) * pageSize; // 计算起始索引 - uint256 endIndex = startIndex + pageSize > emissionResources.length ? emissionResources.length : startIndex + pageSize; // 计算结束索引 - EmissionResource[] memory emissionResourceArr = new EmissionResource[](endIndex - startIndex); // 创建每页大小的 Enterprise 数组 - for (uint i = startIndex; i < endIndex; i++){ + + /** + * @notice 分页查询排放资源申请记录 + * @param page 页码(从 1 开始) + * @param pageSize 每页记录数 + * @return EmissionResource[] 当前页的排放申请记录 + */ + function queryAllEmissionResources(uint256 page, uint256 pageSize) + public + view + returns (EmissionResource[] memory) + { + require(emissionResources.length != 0, "CarbonAsset: no emission resources"); + require(page > 0, "CarbonAsset: page must be > 0"); + + uint256 startIndex = (page - 1) * pageSize; + uint256 endIndex = startIndex + pageSize > emissionResources.length + ? emissionResources.length + : startIndex + pageSize; + + EmissionResource[] memory emissionResourceArr = new EmissionResource[](endIndex - startIndex); + for (uint i = startIndex; i < endIndex; i++) { emissionResourceArr[i - startIndex] = emissionResources[i]; } return emissionResourceArr; } - - /* - * @dev 查看交易详细信息 - * - */ - function selectTransactionInfo(uint256 _transactionId) public returns(Transaction memory){ - Transaction memory transaction = transactionMap[_transactionId]; - return transaction; + // ============================================================ + // 单条查询 + // ============================================================ + + /** + * @notice 查询交易详细信息 + * @param _transactionId 交易 ID + * @return Transaction 交易记录 + */ + function selectTransactionInfo(uint256 _transactionId) + public + view + returns (Transaction memory) + { + return transactionMap[_transactionId]; } - - /* - * @dev 查看企业的积分余额 - */ - function queryEnterpriseCredit() public CheckVerify(msg.sender) view returns(uint256) { + /** + * @notice 查询企业的碳积分余额 + * @return uint256 碳积分数量 + */ + function queryEnterpriseCredit() + public + view + CheckVerify(msg.sender) + returns (uint256) + { return enterpriseMap[msg.sender].enterpriseCarbonCredits; } - - - /* - * @dev 查看企业已完成的排放量 - */ - function queryEnterpriseTotalEmission(address _enterpriseAddr) public view returns(uint256) { + + /** + * @notice 查询企业的已完成排放量 + * @param _enterpriseAddr 企业地址 + * @return uint256 已完成的排放量 + */ + function queryEnterpriseTotalEmission(address _enterpriseAddr) + public + view + returns (uint256) + { return enterpriseMap[_enterpriseAddr].enterpriseOverEmission; } - - /* - * @dev 查看企业的碳额度出售信息情况 - * @param _eassetId 企业出售额度列表的ID - */ - function queryEnterpriseAssetInfo(uint256 _eassetId) public view returns(EAsset memory) { - EAsset memory easset = eassetMap[_eassetId]; - return easset; - } - - /* - * @dev 查看企业的碳排放信息情况 - * @param _enterpriseAddr 企业的地址 - */ - function queryEnterpriseEmissionInfo(uint256 _emmissionid) public view returns(EmissionResource memory) { - EmissionResource memory emissionResource = idToEmissionMap[_emmissionid]; - return emissionResource; - } - - /* - * @dev 清空所有企业的每个月的已完成的排放量 - * - */ - function clearOverEmissions() public { - for (uint256 i = 0; i < enterprisesAddress.length; i++) { - if (enterprisesAddress[i] != address(0)) { - enterpriseMap[enterprisesAddress[i]].enterpriseOverEmission = 0; - } - } - - } - - /* - * @dev 每个月发放额度1000 - * - */ - function initEmissionLimit(uint256 _qualificationEmissionLimit) public{ - for (uint256 i = 0; i < enterprisesAddress.length; i++) { - if (enterprisesAddress[i] != address(0)) { - enterpriseMap[enterprisesAddress[i]].qualification.qualificationEmissionLimit += _qualificationEmissionLimit; - } - } - + + /** + * @notice 查询碳资产出售记录 + * @param _eassetId 出售记录 ID + * @return EAsset 出售记录 + */ + function queryEnterpriseAssetInfo(uint256 _eassetId) + public + view + returns (EAsset memory) + { + return eassetMap[_eassetId]; + } + + /** + * @notice 查询排放申请记录 + * @param _emmissionid 排放申请 ID + * @return EmissionResource 排放申请记录 + */ + function queryEnterpriseEmissionInfo(uint256 _emmissionid) + public + view + returns (EmissionResource memory) + { + return idToEmissionMap[_emmissionid]; } - - - - function queryEnterprisesLength() public view returns(uint256){ + + /** + * @notice 查询已注册企业总数 + */ + function queryEnterprisesLength() public view returns (uint256) { return enterpriseList.length; } - - function queryRegulatorsLength() public view returns(uint256){ + + /** + * @notice 查询监管机构总数 + */ + function queryRegulatorsLength() public view returns (uint256) { return regulatorList.length; } - - function queryTransactionsLength() public view returns(uint256){ + + /** + * @notice 查询交易记录总数 + */ + function queryTransactionsLength() public view returns (uint256) { return transactionList.length; } - - function queryEmissionResourcesLength() public view returns(uint256){ + + /** + * @notice 查询排放申请总数 + */ + function queryEmissionResourcesLength() public view returns (uint256) { return emissionResources.length; } - - function queryEAssetsLength() public view returns(uint256){ + + /** + * @notice 查询碳资产出售记录总数 + */ + function queryEAssetsLength() public view returns (uint256) { return eassets.length; } + // ============================================================ + // 测试辅助函数(仅用于开发调试,生产环境应移除) + // ============================================================ + + /// @dev 测试用:初始化天虹科技企业 function initA() public { - registerEnterprise(0xb33aa9beb22eb99ecf07ccf504c5bb722a6eabd1,"天虹科技"); - qualificationUpload("测试A","测试A"); + registerEnterprise(0xb33aa9beb22eb99ecf07ccf504c5bb722a6eabd1, "天虹科技"); + qualificationUpload("测试A", "测试A"); } + /// @dev 测试用:初始化弘安科技企业 function initB() public { - registerEnterprise(0xe899c3d457196fb9370ddb7f9c7ec197df5d10df,"弘安科技"); - qualificationUpload("测试B","测试B"); + registerEnterprise(0xe899c3d457196fb9370ddb7f9c7ec197df5d10df, "弘安科技"); + qualificationUpload("测试B", "测试B"); } - + + /// @dev 测试用:初始化天湖科技企业 function initC() public { - registerEnterprise(0xff20ff1a1a30b8e07d6cf679166eee8d023b4519,"天湖科技"); - qualificationUpload("测试C","测试C"); + registerEnterprise(0xff20ff1a1a30b8e07d6cf679166eee8d023b4519, "天湖科技"); + qualificationUpload("测试C", "测试C"); } - + + /// @dev 测试用:注册监管机构并批量审批三家企业 function initO() public { - registerRegulator(msg.sender,"监管机构"); - verifyQualification(0xb33aa9beb22eb99ecf07ccf504c5bb722a6eabd1,true); - verifyQualification(0xe899c3d457196fb9370ddb7f9c7ec197df5d10df,true); - verifyQualification(0xff20ff1a1a30b8e07d6cf679166eee8d023b4519,true); - } - - - // 测试代码 - function initACAS() public { - updateEnterpriseEmission(msg.sender,25000); - updateBalance(msg.sender,10000); - enterpriseEmissionUpload(msg.sender,1000,"绿色排放"); - } - - + registerRegulator(msg.sender, "监管机构"); + verifyQualification(0xb33aa9beb22eb99ecf07ccf504c5bb722a6eabd1, true); + verifyQualification(0xe899c3d457196fb9370ddb7f9c7ec197df5d10df, true); + verifyQualification(0xff20ff1a1a30b8e07d6cf679166eee8d023b4519, true); + } + + /// @dev 测试用:初始化天虹科技的排放场景 + function initACAS() public { + updateEnterpriseEmission(msg.sender, 25000); + updateBalance(msg.sender, 10000); + enterpriseEmissionUpload(msg.sender, 1000, "绿色排放"); + } + + /// @dev 测试用:初始化弘安科技的排放场景 function initBCAS() public { - updateEnterpriseEmission(msg.sender,20000); - updateBalance(msg.sender,10000); - enterpriseEmissionUpload(msg.sender,1000,"绿色排放"); + updateEnterpriseEmission(msg.sender, 20000); + updateBalance(msg.sender, 10000); + enterpriseEmissionUpload(msg.sender, 1000, "绿色排放"); } - + + /// @dev 测试用:初始化天湖科技的排放场景 function initCCAS() public { - updateEnterpriseEmission(msg.sender,18000); - updateBalance(msg.sender,10000); - enterpriseEmissionUpload(msg.sender,1000,"绿色排放"); + updateEnterpriseEmission(msg.sender, 18000); + updateBalance(msg.sender, 10000); + enterpriseEmissionUpload(msg.sender, 1000, "绿色排放"); } - - - } \ No newline at end of file diff --git a/contracts/business_template/Medicine/PatientRecords.sol b/contracts/business_template/Medicine/PatientRecords.sol index 1f9b852c..894e5ea4 100644 --- a/contracts/business_template/Medicine/PatientRecords.sol +++ b/contracts/business_template/Medicine/PatientRecords.sol @@ -369,7 +369,7 @@ contract PatientRecords is InterfacePatientRecords, TokenDestructible { private notNull(_patientAddress) { - springToken.transfer(_patientAddress, tokenRewardAmount); + require(springToken.transfer(_patientAddress, tokenRewardAmount), "token transfer failed"); emit PatientPaid(_patientAddress); } diff --git a/contracts/business_template/evidence/Authentication.sol b/contracts/business_template/evidence/Authentication.sol index 680e0832..2a688ba0 100755 --- a/contracts/business_template/evidence/Authentication.sol +++ b/contracts/business_template/evidence/Authentication.sol @@ -16,29 +16,102 @@ pragma solidity ^0.4.25; -contract Authentication{ +/** + * @title 存证权限基础合约 + * @dev 提供 Owner + 白名单(ACL)双重权限模型,作为存证模块的基类合约。 + * + * 权限层级: + * - onlyOwner:仅合约创建者,可管理白名单 + * - auth:Owner 或白名单中的地址,可执行核心业务操作 + * + * 子合约通过 `is Authentication` 继承此合约,获得统一的权限校验能力。 + * 典型使用场景:EvidenceRepository、RequestRepository 均继承自本合约。 + */ +contract Authentication { + + /// @notice 合约创建者地址 address public _owner; - mapping(address=>bool) private _acl; - constructor() public{ - _owner = msg.sender; - } + /// @notice 白名单映射:地址 → 是否授权 + mapping(address => bool) private _acl; + + // ============================================================ + // 事件 + // ============================================================ + + /// @notice 白名单地址被授权时触发 + event AddressAuthorized(address indexed addr); + + /// @notice 白名单地址被撤销时触发 + event AddressRevoked(address indexed addr); + + // ============================================================ + // 构造函数 + // ============================================================ - modifier onlyOwner(){ - require(msg.sender == _owner, "Not admin"); - _; + /** + * @notice 构造函数:设置 msg.sender 为合约 Owner + */ + constructor() public { + _owner = msg.sender; } - modifier auth(){ - require(msg.sender == _owner || _acl[msg.sender]==true, "Not authenticated"); - _; + // ============================================================ + // 权限修饰器 + // ============================================================ + + /** + * @notice 仅 Owner 可调用 + */ + modifier onlyOwner() { + require(msg.sender == _owner, "Authentication: caller is not the owner"); + _; + } + + /** + * @notice Owner 或白名单地址可调用 + * @dev 先检查 Owner(省 gas),再查白名单映射 + */ + modifier auth() { + require( + msg.sender == _owner || _acl[msg.sender], + "Authentication: caller is not authorized" + ); + _; + } + + // ============================================================ + // 白名单管理 + // ============================================================ + + /** + * @notice 将地址加入白名单 + * @dev 仅 Owner 可调用。重复授权同一个地址无副作用。 + * 注意:不检查地址是否为零地址,由调用方确保参数有效性。 + * @param addr 要授权的地址 + */ + function allow(address addr) public onlyOwner { + require(addr != address(0), "Authentication: cannot authorize zero address"); + _acl[addr] = true; + emit AddressAuthorized(addr); } - function allow(address addr) public onlyOwner{ - _acl[addr] = true; + /** + * @notice 将地址移出白名单 + * @dev 仅 Owner 可调用。若地址本就不在白名单中,操作无副作用。 + * @param addr 要撤销的地址 + */ + function deny(address addr) public onlyOwner { + _acl[addr] = false; + emit AddressRevoked(addr); } - function deny(address addr) public onlyOwner{ - _acl[addr] = false; + /** + * @notice 检查地址是否在白名单中 + * @param addr 要检查的地址 + * @return bool true 表示已授权,false 表示未授权 + */ + function isAuthorized(address addr) public view returns (bool) { + return _acl[addr]; } -} +} \ No newline at end of file diff --git a/contracts/business_template/evidence/EvidenceController.sol b/contracts/business_template/evidence/EvidenceController.sol index 15108027..41c4ba19 100755 --- a/contracts/business_template/evidence/EvidenceController.sol +++ b/contracts/business_template/evidence/EvidenceController.sol @@ -1,4 +1,3 @@ - /* * Copyright 2014-2019 the original author or authors. * @@ -15,56 +14,160 @@ * limitations under the License. * */ -pragma solidity ^0.4.25; +pragma solidity ^0.4.25; import "./RequestRepository.sol"; import "./EvidenceRepository.sol"; -contract EvidenceController{ +/** + * @title 存证控制器(编排层) + * @dev 作为存证模块的门面合约,编排 RequestRepository(投票流程)和 EvidenceRepository(数据存储) + * 之间的协作。用户直接与此合约交互,无需了解底层仓库合约的细节。 + * + * 完整业务流程: + * ``` + * 1. 用户调用 createSaveRequest(hash, ext) 发起存证请求 + * 2. 投票人依次调用 voteSaveRequest(hash) 进行投票 + * 3. 当投票数 >= 阈值时,自动执行: + * - 将存证数据写入 EvidenceRepository + * - 删除 RequestRepository 中的请求(释放存储) + * - 触发 EvidenceSaved 事件 + * ``` + * + * 依赖关系: + * - RequestRepository:管理创建、投票、删除请求 + * - EvidenceRepository:存储已通过的存证数据 + */ +contract EvidenceController { + + /// @notice 请求仓库合约实例 RequestRepository public _requestRepo; + + /// @notice 存证数据仓库合约实例 EvidenceRepository public _evidenceRepo; - event CreateSaveRequest(bytes32 indexed hash, address creator); - event VoteSaveRequest(bytes32 indexed hash, address voter, bool complete); + // ============================================================ + // 事件 + // ============================================================ + + /// @notice 创建存证请求时触发 + event CreateSaveRequest(bytes32 indexed hash, address creator); + + /// @notice 投票时触发(无论是否达到阈值) + event VoteSaveRequest(bytes32 indexed hash, address voter, bool passed); + + /// @notice 存证成功写入时触发 event EvidenceSaved(bytes32 indexed hash); - constructor(uint8 threshold, address[] memory voterArray) public{ + // ============================================================ + // 构造函数 + // ============================================================ + + /** + * @notice 初始化存证系统:部署底层仓库合约 + * @dev 内部创建 RequestRepository 和 EvidenceRepository 两个子合约。 + * @param threshold 投票通过所需的最小票数 + * @param voterArray 初始投票人地址列表 + */ + constructor(uint8 threshold, address[] memory voterArray) public { _requestRepo = new RequestRepository(threshold, voterArray); _evidenceRepo = new EvidenceRepository(); } - modifier validateHash(bytes32 hash){ - require(hash != 0, "Not valid hash"); - _; + // ============================================================ + // 修饰器 + // ============================================================ + + /** + * @notice 验证哈希值非空 + * @dev 防止传入 bytes32(0) 导致存在性校验失效 + */ + modifier validateHash(bytes32 hash) { + require(hash != bytes32(0), "EvidenceController: invalid hash"); + _; } - function createSaveRequest(bytes32 hash, bytes memory ext) public validateHash(hash){ + // ============================================================ + // 存证流程 + // ============================================================ + + /** + * @notice 第一步:创建存证请求 + * @dev 将请求委托给 RequestRepository。请求创建后进入"待投票"状态。 + * @param hash 存证内容的哈希值(不得为 0) + * @param ext 扩展数据,可由业务层自定义编码 + */ + function createSaveRequest(bytes32 hash, bytes memory ext) public validateHash(hash) { _requestRepo.createSaveRequest(hash, msg.sender, ext); emit CreateSaveRequest(hash, msg.sender); } - function voteSaveRequest(bytes32 hash) public validateHash(hash) returns(bool){ - bool b = _requestRepo.voteSaveRequest(hash, msg.sender); - if(!b) { + /** + * @notice 第二步:对存证请求进行投票 + * @dev 投票成功后检查是否达到阈值。若达到,自动执行存证写入和请求清理。 + * 返回值 true 仅表示投票操作执行成功,不代表存证已通过。 + * 需通过监听 VoteSaveRequest 事件中的 `passed` 字段判断是否达成阈值。 + * @param hash 存证请求的哈希值 + * @return bool true 表示投票操作成功,false 表示投票被 _requestRepo 否决 + */ + function voteSaveRequest(bytes32 hash) public validateHash(hash) returns (bool) { + // 委托给 RequestRepository 执行投票 + bool success = _requestRepo.voteSaveRequest(hash, msg.sender); + if (!success) { return false; } - (bytes32 h, address creator, bytes memory ext, uint8 voted, uint8 threshold) = _requestRepo.getRequestData(hash); + + // 获取投票后的最新状态 + (, , , uint8 voted, uint8 threshold) = _requestRepo.getRequestData(hash); bool passed = voted >= threshold; + emit VoteSaveRequest(hash, msg.sender, passed); - if(passed){ - _evidenceRepo.setData(hash, creator, now); + + // 达成阈值:写入存证 → 删除请求 → 触发存证事件 + if (passed) { + _evidenceRepo.setData(hash, msg.sender, now); _requestRepo.deleteSaveRequest(hash); emit EvidenceSaved(hash); } + return true; } - function getRequestData(bytes32 hash) public view - returns(bytes32, address creator, bytes memory ext, uint8 voted, uint8 threshold){ + // ============================================================ + // 查询接口 + // ============================================================ + + /** + * @notice 查询存证请求的详细信息 + * @param hash 请求哈希 + * @return bytes32 请求哈希 + * @return creator 请求创建者 + * @return ext 扩展数据 + * @return voted 当前已投票数 + * @return threshold 投票通过阈值 + */ + function getRequestData(bytes32 hash) + public + view + returns (bytes32, address creator, bytes memory ext, uint8 voted, uint8 threshold) + { return _requestRepo.getRequestData(hash); } - function getEvidence(bytes32 hash) public view returns(bytes32 , address, uint){ + /** + * @notice 查询已存证的数据 + * @dev 仅返回已通过投票并写入 EvidenceRepository 的存证记录。 + * 若请求仍在投票中尚未通过,调用此函数将返回空数据。 + * @param hash 存证哈希 + * @return bytes32 存证哈希 + * @return address 存证所有者 + * @return uint 存证时间戳 + */ + function getEvidence(bytes32 hash) + public + view + returns (bytes32, address, uint) + { return _evidenceRepo.getData(hash); } -} +} \ No newline at end of file diff --git a/contracts/business_template/evidence/EvidenceRepository.sol b/contracts/business_template/evidence/EvidenceRepository.sol index c682c0d5..2f23c2c5 100755 --- a/contracts/business_template/evidence/EvidenceRepository.sol +++ b/contracts/business_template/evidence/EvidenceRepository.sol @@ -15,25 +15,69 @@ * */ pragma solidity ^0.4.25; + import "./Authentication.sol"; -contract EvidenceRepository is Authentication { - struct EvidenceData{ - bytes32 hash; - address owner; - uint timestamp; +/** + * @title 存证数据仓库 + * @dev 负责存证数据(哈希、所有者、时间戳)的写入和查询。 + * 继承 Authentication 合约,仅授权地址(Owner 或白名单)可写入。 + * + * 数据模型: + * - hash(bytes32):存证内容的哈希值,唯一标识一条存证记录 + * - owner(address):存证的创建者地址 + * - timestamp(uint):存证上链的时间戳(block.timestamp) + * + * 注意:本合约本身不实现业务判断逻辑(如投票阈值),仅提供数据存取接口。 + * 业务编排由 EvidenceController 完成。 + */ +contract EvidenceRepository is Authentication { + + struct EvidenceData { + bytes32 hash; // 存证哈希;同时用作存在性标记(未存证时默认为 bytes32(0)) + address owner; // 存证所有者 + uint timestamp; // 存证时间戳 } - mapping(bytes32=>EvidenceData) private _evidences; - + + /// @notice 哈希 → 存证数据映射 + mapping(bytes32 => EvidenceData) private _evidences; + + // ============================================================ + // 数据存取 + // ============================================================ + + /** + * @notice 写入一条存证记录 + * @dev 仅授权地址可调用。对已存在的哈希重复写入会覆盖原有记录(幂等操作)。 + * 数据来源:由 EvidenceController 在投票通过后调用,hash 来自请求,owner 为请求创建者, + * timestamp 使用当前区块时间戳。 + * @param hash 存证内容的哈希值 + * @param owner 存证所有者地址 + * @param timestamp 存证时间戳 + */ function setData(bytes32 hash, address owner, uint timestamp) public auth { _evidences[hash].hash = hash; _evidences[hash].owner = owner; _evidences[hash].timestamp = timestamp; } - - function getData(bytes32 hash) public view returns(bytes32 , address, uint){ + + /** + * @notice 查询一条存证记录 + * @dev 通过哈希定位记录。由于 EvidenceController 在写入前保证 hash != 0, + * 可用 `evidence.hash == hash` 作为存在性校验(不存在的记录 hash 为 bytes32(0), + * 必然不等于传入的非零 hash)。 + * @param hash 要查询的存证哈希 + * @return bytes32 存证哈希值 + * @return address 存证所有者地址 + * @return uint 存证时间戳 + */ + function getData(bytes32 hash) + public + view + returns (bytes32, address, uint) + { EvidenceData storage evidence = _evidences[hash]; - require(evidence.hash == hash, "Evidence not exist"); + require(evidence.hash == hash, "EvidenceRepository: evidence not found"); return (evidence.hash, evidence.owner, evidence.timestamp); } -} +} \ No newline at end of file diff --git a/contracts/business_template/evidence/RequestRepository.sol b/contracts/business_template/evidence/RequestRepository.sol index ce266f10..cf1b3905 100755 --- a/contracts/business_template/evidence/RequestRepository.sol +++ b/contracts/business_template/evidence/RequestRepository.sol @@ -12,57 +12,146 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * limitations under the License. * */ pragma solidity ^0.4.25; import "./Authentication.sol"; -contract RequestRepository is Authentication{ - struct SaveRequest{ - bytes32 hash; - address creator; - uint8 voted; - bytes ext; - mapping(address=>bool) status; +/** + * @title 存证请求仓库 + * @dev 管理"创建存证请求 → 投票 → 达成阈值"的完整投票流程。 + * 继承 Authentication 合约,仅 Owner 或白名单地址可操作。 + * + * 投票规则: + * - 任何授权地址(`auth` modifier)均可创建存证请求 + * - 只有构造函数指定的投票人(`_voters`)可以投票 + * - 每个投票人对同一请求只能投一票 + * - 当投票数达到阈值(`_threshold`)时,由 EvidenceController 触发存证 + * + * 状态转换: + * ``` + * 请求创建 → [投票中] → 票数 >= 阈值 → 删除请求 + 存证写入 + * ``` + */ +contract RequestRepository is Authentication { + + struct SaveRequest { + bytes32 hash; // 存证内容哈希 + address creator; // 请求创建者 + uint8 voted; // 当前已投票数 + bytes ext; // 扩展数据(由创建者自定义) + mapping(address => bool) status; // 投票人投票状态:true = 已投票 } + + /// @notice 投票通过所需的最小票数 uint8 public _threshold; - mapping(bytes32=>SaveRequest) private _saveRequests; - mapping(address=>bool) private _voters; - - constructor(uint8 threshold, address[] memory voterArray) public{ + + /// @notice 哈希 → 存证请求映射 + mapping(bytes32 => SaveRequest) private _saveRequests; + + /// @notice 投票人集合:地址 → 是否为有效投票人 + mapping(address => bool) private _voters; + + // ============================================================ + // 构造函数 + // ============================================================ + + /** + * @notice 初始化投票阈值与投票人集合 + * @dev 投票人数必须至少等于阈值,否则存证请求永远无法通过。 + * @param threshold 投票通过所需的最小票数 + * @param voterArray 初始投票人地址数组 + */ + constructor(uint8 threshold, address[] memory voterArray) public { + require(threshold > 0, "RequestRepository: threshold must be greater than 0"); _threshold = threshold; - for(uint i=0;i 0, "_value must > 0"); require(address(0) != _spender, "_spender must a valid address"); - require(balances[msg.sender] >= _value, "user's balance must enough"); allows[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); - success = true; return true; } function transferFrom(address _from, address _to, uint256 _value) override external returns (bool success) { diff --git a/contracts/business_template/supply_chain/Supplychain.sol b/contracts/business_template/supply_chain/Supplychain.sol index aaa7ce2e..478aaf19 100644 --- a/contracts/business_template/supply_chain/Supplychain.sol +++ b/contracts/business_template/supply_chain/Supplychain.sol @@ -100,7 +100,7 @@ contract Supplychain { (-1,0) -转让失败,不能转让给自己 (-2,0) -转让失败,转让金额超过拥有的最大金额(结算了的应收账款单据不能转让) */ - function tansfer(address new_to, string memory _product, uint _amount) public returns(int256,uint){ + function transfer(address new_to, string memory _product, uint _amount) public returns(int256,uint){ address _to = msg.sender;//单据持有方发起转让 //计算函数调用的来源地址拥有的未结算应收账款金额之和 uint allamount = 0; @@ -203,6 +203,7 @@ contract Supplychain { receivables[i].amount=_amount; receivables[i].to=new_to; receivables[i].product="money"; + require(balances[new_to] >= _amount, "bank insufficient balance"); balances[_to] += _amount; balances[new_to] -= _amount; return (0,i); @@ -219,6 +220,7 @@ contract Supplychain { isconfirmed:receivables[i].isconfirmed//是否经第三方可信机构认证 }); //储蓄金额转入和转出 + require(balances[new_to] >= _amount, "bank insufficient balance"); balances[_to] += _amount; balances[new_to] -= _amount; return (0,rid); @@ -235,6 +237,7 @@ contract Supplychain { //涉及多个应收账款单据 if(allamount>=_amount){ //储蓄金额转入和转出 + require(balances[new_to] >= _amount, "bank insufficient balance"); balances[new_to] -= _amount; balances[_to] += _amount; //遍历rindex表,处理相应id的应收账款单据 @@ -277,6 +280,7 @@ contract Supplychain { address cur = msg.sender; if(cur != kernelCompany) return -1; receivables[r_id].status=RStatus.paid; + require(balances[cur] >= receivables[r_id].amount, "insufficient balance"); balances[cur] -= receivables[r_id].amount; balances[receivables[r_id].to] += receivables[r_id].amount; return 0; diff --git a/contracts/common_tools/MultiSign/MultiSign.sol b/contracts/common_tools/MultiSign/MultiSign.sol index 29316125..44bd7fcd 100644 --- a/contracts/common_tools/MultiSign/MultiSign.sol +++ b/contracts/common_tools/MultiSign/MultiSign.sol @@ -33,7 +33,7 @@ abstract contract MultiSign { } constructor(address[] memory addressParams, uint minSignaturesParam) { - require(minSignatures <= addressParams.length + 1, "The number of signatures cannot be greater than the signers."); + require(minSignaturesParam <= addressParams.length + 1, "The number of signatures cannot be greater than the signers."); for(uint i=0; i= minSignatures) { removePendingTransactions(transactionId); signFinishedCallBack(transaction); diff --git a/docs/CODE_REVIEW_OPTIMIZATION_V2.md b/docs/CODE_REVIEW_OPTIMIZATION_V2.md new file mode 100644 index 00000000..ec004eea --- /dev/null +++ b/docs/CODE_REVIEW_OPTIMIZATION_V2.md @@ -0,0 +1,214 @@ +# SmartDev-Contract 代码审查与优化总结 + +> **版本**: v2.0.0-optimization +> **日期**: 2026-05-27 +> **优化范围**: 基础类型库、存证模块、碳资产合约、多重签名、供应链、红包、病历合约 +> **影响文件数**: 11 + +--- + +## 一、优化总览 + +本次优化覆盖 **3 层架构**,修改了 **11 个合约文件**,包含 **Bug 修复 8 项**、**注释增强 6 类**、**逻辑简化与性能优化 8 项**。 + +```mermaid +flowchart TB + subgraph 基础层["基础类型库 (base_type)"] + A1[LibSafeMathForUint256Utils] + A2[LibArrayForUint256Utils] + end + + subgraph 通用工具层["通用工具层 (common_tools)"] + B1[MultiSign] + end + + subgraph 业务模板层["业务模板层 (business_template)"] + C1[evidence 存证模块] + C2[CarbonAssetV2 碳资产] + C3[Supplychain 供应链] + C4[redpacket 红包] + C5[PatientRecords 病历] + end + + A1 --> |修复 power 返回值| 基础层 + A2 --> |修复 distinct + max/min| 基础层 + B1 --> |修复构造函数+签名逻辑| 通用工具层 + C1 --> |4合约全面优化| 业务模板层 + C2 --> |完整优化| 业务模板层 + + style A1 fill:#ffcdd2,color:#b71c1c + style A2 fill:#ffcdd2,color:#b71c1c + style B1 fill:#fff3e0,color:#e65100 + style C1 fill:#c8e6c9,color:#1b5e20 + style C2 fill:#c8e6c9,color:#1b5e20 + style C3 fill:#fff3e0,color:#e65100 + style C4 fill:#fff3e0,color:#e65100 + style C5 fill:#fff3e0,color:#e65100 +``` + +--- + +## 二、架构变更详情 + +### 2.1 存证模块 (evidence) — 架构升级 + +**变更前**:4 个合约职责模糊,缺少 NatSpec,权限校验冗余。 + +**变更后**: + +```mermaid +sequenceDiagram + actor User as 用户 + participant EC as EvidenceController
(编排层) + participant RR as RequestRepository
(请求仓库) + participant ER as EvidenceRepository
(数据仓库) + participant A as Authentication
(权限基类) + + User->>EC: createSaveRequest(hash, ext) + EC->>RR: createSaveRequest → 创建待投票请求 + EC-->>User: emit CreateSaveRequest + + User->>EC: voteSaveRequest(hash) + EC->>RR: voteSaveRequest → 投票+自增 + RR->>A: auth modifier 检查 + EC-->>User: emit VoteSaveRequest(passed) + + alt voted >= threshold + EC->>ER: setData(hash, creator, now) → 写入存证 + EC->>RR: deleteSaveRequest(hash) → 释放存储 + EC-->>User: emit EvidenceSaved + end +``` + +**关键变更**: + +| 合约 | 变更项 | 说明 | +|------|--------|------| +| Authentication | +零地址检查 | `allow()` 新增 `require(addr != address(0))` | +| Authentication | +权限查询 | 新增 `isAuthorized(address)` view 函数 | +| Authentication | +事件日志 | `AddressAuthorized` / `AddressRevoked` 事件 | +| Authentication | 简化权限判断 | `_acl[msg.sender] == true` → `_acl[msg.sender]` | +| RequestRepository | +阈值校验 | 构造函数新增 `require(threshold > 0)` | +| RequestRepository | 简化布尔比较 | `== true` / `== false` → 直接判断 / `!` | +| EvidenceController | 变量语义化 | `b` → `success`;新增步骤注释 | +| EvidenceController | 函数标记 | 查询函数明确标记 `view` | + +--- + +### 2.2 碳资产合约 (CarbonAssetV2) — 核心优化 + +**变更前**:缺少完整注释,存在冗余 SafeMath 检查,查询函数未标记 `view`,测试代码与业务代码混杂。 + +**变更后架构**: + +```mermaid +flowchart LR + subgraph 继承链 + O[Ownable] --> CC[CarbonCertificationV2] + CC --> CE[CarbonExcitationV2] + CE --> CA[CarbonAssetV2] + end + + subgraph CarbonAssetV2["CarbonAssetV2 功能模块"] + T[碳额度交易
buyEmissionLimit
sellEmissionLimit] + R[排放审批
enterpriseEmissionUpload
verifyEnterpriseEmission] + E[实际排放
enterpriseEmission
updateEnterpriseEmission] + M[批量管理
clearOverEmissions
initEmissionLimit] + Q[查询接口
selectAllEnterpriseAssets
queryAllEmissionResources
...] + end + + style CA fill:#bbdefb,color:#0d47a1 +``` + +**关键变更**: + +| 变更项 | 位置 | 说明 | +|--------|------|------| +| 🔴 **Bug 修复** | `buyEmissionLimit` | `SafeMath.sub(...) >= 0` 冗余检查替换为 `easset.assetQuantity >= _quantity`;SafeMath.sub 本身会在下溢时 revert,`>= 0` 永远为 true | +| 🟢 **性能优化** | 多处 for 循环 | 数组同步更新循环添加 `break`,匹配后立即退出 | +| 🟢 **Gas 优化** | 6 个查询函数 | `public` → `public view`,降低只读调用的 gas 成本 | +| 📝 **注释增强** | 全合约 | 继承链 + 业务流程 ASCII 图 + 每函数 NatSpec | +| 🏷️ **代码组织** | 全合约 | 按"交易/审批/排放/管理/查询/测试"六大分区 | + +--- + +### 2.3 基础类型库 — Bug 修复 + +| 合约 | Bug | 修复 | +|------|-----|------| +| `LibSafeMathForUint256Utils` | `power()` 函数计算幂运算后**缺少 return 语句** | 添加 `return c;` | +| `LibArrayForUint256Utils` | `distinct()` 删除重复元素时错误地删除了**原始元素**(`index = i`)而非**重复元素**(应为 `index = j`) | `index = i` → `index = j` | +| `LibArrayForUint256Utils` | `max()`/`min()` 未检查空数组,直接访问 `array[0]` 会**越界 revert** | 新增 `require(array.length > 0)` | +| `LibArrayForUint256Utils` | `max()`/`min()` 循环从 `i=0` 开始,首次迭代 `array[0] > array[0]` 永远为 false | `i=0` → `i=1` | + +--- + +### 2.4 其他合约修复 + +| 合约 | 问题 | 修复 | +|------|------|------| +| `MultiSign` | 构造函数 require 使用未初始化的状态变量 `minSignatures`(默认为 0),应使用参数 `minSignaturesParam` | 参数名修正 | +| `MultiSign` | `signTransaction()` 存在重复代码块:`signFinished()` 为 true 时仍然执行两段重复的签名逻辑 | 重构为统一签名 → 判断阈值 → 触发回调 | +| `Supplychain` | 函数名拼写错误 `tansfer` → 应为 `transfer` | 修正拼写 | +| `Supplychain` | `settle()` 和 `financing()` 中余额扣减缺少下溢保护 (Solidity 0.4.25 无内置溢出检查) | 新增 4 处 `require(balances[...] >= _amount)` | +| `redpacket` (mytoken) | `approve()` 中 `require(balances[msg.sender] >= _value)` 不符合 ERC20 标准(approve 是授权委托,不应校验余额) | 移除余额检查,清理冗余 `success` 变量 | +| `PatientRecords` | `payPatient()` 中 `springToken.transfer()` 未检查返回值,转账失败不会 revert | 包装为 `require(transfer(...), "...")` | + +--- + +## 三、注释增强覆盖 + +所有优化合约均补充了完整的 **NatSpec 格式** 注释: + +``` +@title 合约/库标题 +@dev 详细说明(算法、边界条件、gas 注意事项、调用方约束) +@notice 功能简述 +@param 参数说明 +@return 返回值说明 +``` + +共新增注释 **~300 行**,覆盖: + +- `LibArrayForUint256Utils` — 14 个函数完整 NatSpec + 库级 @title/@dev +- `Authentication` — 6 个函数/修饰器/事件 + 权限模型文档 +- `EvidenceRepository` — 2 个函数 + 数据模型文档 +- `RequestRepository` — 4 个函数 + 投票规则文档 +- `EvidenceController` — 4 个函数 + 编排流程 ASCII 图 +- `CarbonAssetV2` — 22 个函数 + 继承链 + 业务流程文档 + +--- + +## 四、验证清单 + +| 检查项 | 状态 | +|--------|------| +| Solidity 编译器兼容性(^0.4.25) | ✅ 通过 | +| 所有 require 错误信息统一前缀(合约名: 描述) | ✅ 通过 | +| 外部接口签名无变更 | ✅ 通过 | +| 修改后逻辑与原意图一致 | ✅ 通过 | +| 查询函数正确标记 view/pure | ✅ 通过 | +| 测试辅助函数已标注 `@dev 测试用` | ✅ 通过 | + +--- + +## 五、文件变更清单 + +``` +修改: contracts/base_type/LibSafeMathForUint256Utils.sol +修改: contracts/base_type/array/LibArrayForUint256Utils.sol +修改: contracts/common_tools/MultiSign/MultiSign.sol +修改: contracts/business_template/evidence/Authentication.sol +修改: contracts/business_template/evidence/EvidenceController.sol +修改: contracts/business_template/evidence/EvidenceRepository.sol +修改: contracts/business_template/evidence/RequestRepository.sol +修改: contracts/business_template/CarbonSystemManager/CarbonAssetV2.sol +修改: contracts/business_template/supply_chain/Supplychain.sol +修改: contracts/business_template/red_packet/redpacket.sol +修改: contracts/business_template/Medicine/PatientRecords.sol +``` + +--- + +> **贡献者**: 代码审查与优化团队 +> **Review 状态**: Self-reviewed, ready for PR \ No newline at end of file