广州科贸区块链智能合约教程-食品溯源

源码

Distributor.sol

FoodInfoItem.sol

Producer.sol

Retailer.sol

Roles.sol

Trace.sol

智能合约功能需求文档:

  1. 具备合约用户管理功能,包括创建用户以及查询用户信息的功能,与其相关联的需要具备用户权限功能。

  2. 具备生产订单管理功能,包括创建与查询订单功能,在创建生产订单功能中限制该功能只能具有生产权限的人员使用,并且加入订单创建时需要与产业链的总订单信息,从而与产业链的其他业务关联。

  3. 具备分销订单管理功能,包括创建与查询订单功能,在创建收购订单功能中限制该功能只能具有分销权限的人员使用,并且加入订单

经销商

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
pragma solidity ^0.4.25;

import "./Roles.sol";

//经销商

contract Distributor {
using Roles for Roles.Role;

event DistributorAdded(address indexed account);
event DistributorRemoved(address indexed account);

Roles.Role private _distributors;

constructor(address distributor) public {
_addDistributor(distributor);
}

modifier onlyDistributor() {
require(
isDistributor(msg.sender),
"DistributorRole: caller does not have the Distributor role"
);
_;
}

function isDistributor(address account) public view returns (bool) {
return _distributors.has(account);
}

function addDistributor(address account) public onlyDistributor {
_addDistributor(account);
}

function renounceDistributor() public {
_removeDistributor(msg.sender);
}

function _addDistributor(address account) internal {
_distributors.add(account);
emit DistributorAdded(account);
}

function _removeDistributor(address account) internal {
_distributors.remove(account);
emit DistributorRemoved(account);
}
}

这段Solidity代码定义了一个名为Distributor的智能合约,该合约主要用于管理具有“经销商”角色的账户。以下是代码的详细解析:

导入依赖

  • import "./Roles.sol";:这行导入了另一个名为Roles的合约,假设这个合约提供了一些关于角色管理的功能,比如添加、移除和检查某个地址是否拥有特定的角色。

合约声明

  • contract Distributor { ... }:定义了一个新的合约Distributor

使用库

  • using Roles for Roles.Role;:这句表示将Roles库的方法直接应用到Roles.Role类型上,使得可以更简洁地使用这些方法(例如直接调用_distributors.add(account))。

事件

  • event DistributorAdded(address indexed account);event DistributorRemoved(address indexed account);:这两个事件分别在成功添加或移除一个经销商时被触发,其中indexed关键字意味着可以在日志中快速查找与特定账户相关的事件。

私有变量

  • Roles.Role private _distributors;:这是一个私有的角色列表,用来存储所有经销商的地址。

构造函数

  • constructor(address distributor) public { _addDistributor(distributor); }:当创建此合同时,需要指定一个初始的经销商,并自动将其加入到经销商名单中。

修饰符

  • modifier onlyDistributor() { ... }:这是一个自定义的访问控制修饰符,确保只有当前是经销商的账号才能执行某些操作。如果不是经销商,则会抛出错误信息。

公共函数

  • function isDistributor(address account) public view returns (bool) { ... }:允许任何人查询给定地址是否为经销商。
  • function addDistributor(address account) public onlyDistributor { ... }:允许现有经销商添加新的经销商。注意这里使用了onlyDistributor修饰符来限制访问权限。
  • function renounceDistributor() public { ... }:允许任何经销商放弃自己的经销商身份。

内部函数

  • _addDistributor(address account) internal { ... }_removeDistributor(address account) internal { ... }:这两个内部函数分别用于向经销商列表中添加或移除地址,并且在这两个动作完成后都会发出相应的事件通知。

总结

整体来看,这个Distributor合约实现了一个简单的角色管理系统,专注于管理经销商的身份认证。它通过引入外部的Roles库简化了角色管理逻辑,同时也提供了必要的功能来维护一个动态更新的经销商列表。这样的设计常见于需要区分不同用户权限的应用场景,如供应链管理、会员制服务等。

食品信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
pragma solidity >=0.4.22 <0.5.0;
pragma experimental ABIEncoderV2;

contract FoodInfoItem {
string _currentTraceName;
string _name;
address _owner;
uint8 _quality;
uint8 _status;
string[] _traceName;
uint256[] _timestamp;
address[] _traceAddress;
uint8[] _traceQuality;

//生产食物
constructor(
string name,
string traceName,
uint8 quality,
address producer
) public {
_currentTraceName = traceName;
_name = name;
_owner = msg.sender;
_quality = quality;
_status = 0;
_traceName.push(traceName);
_timestamp.push(now);
_traceAddress.push(producer);
_traceQuality.push(quality);
}

function addTraceInfoByDistributor(
string traceName,
address distributor,
uint8 quality
) public returns (bool) {
require(_status == 0, "status must be producing");
require(_owner == msg.sender, "only trace contract can invoke");
_timestamp.push(now);
_traceName.push(traceName);
_currentTraceName = traceName;
_traceAddress.push(distributor);
_quality = quality;
_traceQuality.push(_quality);
_status = 1;
return true;
}

function addTraceInfoByRetailer(
string traceName,
address retailer,
uint8 quality
) public returns (bool) {
/* 【9】请在此注释后作答,勿删除此注释*/
//补充食品分销方法
require(_status == 1, "status must be distributor");
require(_owner == msg.sender, "only trace contract can invoke");
_timestamp.push(now);
_traceName.push(traceName);
_currentTraceName = traceName;
_traceAddress.push(retailer);
_quality = quality;
_traceQuality.push(_quality);
_status = 2;
return true;

}

function getTraceInfo()
public
constant
returns (
uint256[],
string[],
address[],
uint8[]
)
{
return (_timestamp, _traceName, _traceAddress, _traceQuality);
}

function getFood()
public
constant
returns (
uint256,
string,
string,
string,
address,
uint8
)
{
return (
_timestamp[0],
_traceName[0],
_name,
_currentTraceName,
_traceAddress[0],
_quality
);
}
}

这段Solidity代码定义了一个名为FoodInfoItem的智能合约,用于追踪食品从生产到分销再到零售的过程。以下是对合约中各个部分的详细解析:

版本声明和特性启用

1
2
pragma solidity >=0.4.22 <0.5.0;
pragma experimental ABIEncoderV2;
  • 第一行声明了合约支持的Solidity版本范围,合约需要编译器版本在0.4.22和0.5.0之间。
  • 第二行启用了ABIEncoderV2实验特性,这允许结构体和数组的返回。

合约定义

1
contract FoodInfoItem {
  • 这里定义了一个名为FoodInfoItem的新合约。

状态变量

1
2
3
4
5
6
7
8
9
string _currentTraceName;
string _name;
address _owner;
uint8 _quality;
uint8 _status;
string[] _traceName;
uint256[] _timestamp;
address[] _traceAddress;
uint8[] _traceQuality;
  • 这些是合约的私有状态变量,用于存储食品信息,包括名称、所有者、质量、状态、追踪过程中的名称、时间戳、地址和质量。

构造函数

1
2
3
4
5
6
7
8
constructor(
string name,
string traceName,
uint8 quality,
address producer
) public {
// 初始化状态变量...
}
  • 构造函数在创建合约实例时执行,用于初始化状态变量。

添加追踪信息的方法(由分销商调用)

1
2
3
4
5
6
7
function addTraceInfoByDistributor(
string traceName,
address distributor,
uint8 quality
) public returns (bool) {
// 添加追踪信息...
}
  • 该方法允许分销商添加食品追踪信息,并更新状态变量。

添加追踪信息的方法(由零售商调用)

1
2
3
4
5
6
7
function addTraceInfoByRetailer(
string traceName,
address retailer,
uint8 quality
) public returns (bool) {
// 补充食品分销方法...
}
  • 该方法允许零售商添加食品追踪信息,并更新状态变量。

获取追踪信息的方法

1
2
3
4
5
6
7
8
9
10
11
12
function getTraceInfo()
public
constant
returns (
uint256[],
string[],
address[],
uint8[]
)
{
// 返回追踪信息...
}
  • 该方法返回食品的完整追踪信息。

获取食品信息的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getFood()
public
constant
returns (
uint256,
string,
string,
string,
address,
uint8
)
{
// 返回食品的初始信息...
}
  • 该方法返回食品的初始信息,如生产时的时间戳、追踪名称、食品名称、当前追踪名称、生产者地址和质量。

注意事项

  • 合约中使用了now,它在Solidity 0.5.0之后被替换为block.timestamp,用于获取当前区块的时间戳。
  • require语句用于验证调用条件,确保合约状态和调用者权限的正确性。

这个合约是一个基本的食品安全追踪系统,它允许追踪食品从生产到分销再到零售的每一个步骤。通过区块链技术,可以确保这些信息的不可篡改性和透明性。

角色库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pragma solidity ^0.4.25;
library Roles {
struct Role {
mapping(address => bool) bearer;
}

function add(Role storage role, address account) internal {
require(!has(role,account),"Role: has a account!");
role.bearer[account] = true;
}

function remove(Role storage role, address account) internal {
require(has(role,account),"Role: has not a account!");
role.bearer[account] = false;
}

function has(Role storage role, address account)
internal
view
returns (bool)
{
require(account != address(0), "Role: has a zero address");
return role.bearer[account];
}



}

这段Solidity代码定义了一个名为Roles的库,它包含了一个用于管理角色的简单数据结构和一些用于操作这些角色的函数。以下是对这段代码的详细解析:

库定义

1
library Roles {
  • 这里定义了一个名为Roles的库,库在Solidity中是一种可以重用的代码方式,它可以被其他合约调用。

角色结构体

1
2
3
struct Role {
mapping(address => bool) bearer;
}
  • Role是一个结构体,它包含了一个映射(mapping),该映射将地址映射到布尔值。这里的bearer映射用于记录拥有特定角色的账户。

添加角色函数

1
2
3
4
function add(Role storage role, address account) internal {
require(!has(role, account), "Role: has a account!");
role.bearer[account] = true;
}
  • add函数用于向给定的角色(role)中添加一个账户(account)。如果账户已经拥有角色,则会抛出错误。internal关键字意味着这个函数只能在当前库或继承了这个库的合约内部调用。

移除角色函数

1
2
3
4
function remove(Role storage role, address account) internal {
require(has(role, account), "Role: has not a account!");
role.bearer[account] = false;
}
  • remove函数用于从给定的角色中移除一个账户。如果账户没有这个角色,则会抛出错误。

检查角色函数

1
2
3
4
5
6
7
8
function has(Role storage role, address account)
internal
view
returns (bool)
{
require(account != address(0), "Role: has a zero address");
return role.bearer[account];
}
  • has函数用于检查一个账户是否拥有特定的角色。它返回一个布尔值,指示账户是否具有该角色。同时,它会检查账户地址是否为零地址,如果是,则抛出错误。

注意事项

  • require语句用于确保函数调用的条件被满足,如果没有,则回滚交易。
  • internal关键字意味着函数只能在内部调用,外部无法直接通过合约接口调用。
  • view关键字表示该函数不会改变合约的状态。

这个库可以被其他合约用来管理不同的角色,例如,一个去中心化应用(DApp)可能会使用这个库来管理管理员、用户、审核员等不同的角色,为不同的角色赋予不同的权限。这样的设计使得权限管理变得更加模块化和可复用。

食品安全追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
pragma solidity >=0.4.22 <0.7.0;
pragma experimental ABIEncoderV2;
import "./FoodInfoItem.sol";
import "./Distributor.sol";
import "./Producer.sol";
import "./Retailer.sol";


//食品工厂合约,负责具体食品溯源信息的生成
contract Trace is Producer, Distributor, Retailer{

mapping (uint256 => address) foods;//食品溯源id到具体食品溯源合约的映射表
uint[] foodList;

//构造函数
constructor(address producer, address distributor, address retailer)
public Producer(producer)
Distributor(distributor)
Retailer(retailer){

}
//生成食品溯源信息接口
//只有Producer能调用
//name 食品名称
//traceNumber 食品溯源id
//traceName 当前用户名称
//quality 当前食品质量
function newFood(string name, uint256 traceNumber, string traceName, uint8 quality)
public onlyProducer returns(address)
{
require(foods[traceNumber] == address(0), "traceNumber already exist");
FoodInfoItem food = new FoodInfoItem(name, traceName, quality, msg.sender);
foods[traceNumber] = food;
foodList.push(traceNumber);
return food;
}

//食品分销过程中增加溯源信息的接口
//只有Distributor能调用
//traceNumber 食品溯源id
//traceName 当前用户名称
//quality 当前食品质量
function addTraceInfoByDistributor(uint256 traceNumber, string traceName, uint8 quality)
public onlyDistributor returns(bool) {
/* 【8】请在此注释后作答,勿删除此注释*/
// 补充完整食品分销过程增加溯源信息的接口
require(foods[traceNumber] != address(0), "traceNumber does not exist");
return FoodInfoItem(foods[traceNumber]).addTraceInfoByDistributor(traceName, msg.sender, quality);

}

//食品出售过程中增加溯源信息的接口
//只有Retailer能调用
//traceNumber 食品溯源id
//traceName 当前用户名称
//quality 当前食品质量
function addTraceInfoByRetailer(uint256 traceNumber, string traceName, uint8 quality)
public onlyRetailer returns(bool) {
require(foods[traceNumber] != address(0), "traceNumber does not exist");
return FoodInfoItem(foods[traceNumber]).addTraceInfoByRetailer(traceName, msg.sender, quality);
}

//获取食品溯源信息接口
//string[] 保存食品流转过程中各个阶段的相关信息
//address[] 保存食品流转过程各个阶段的用户地址信息(和用户一一对应)
//uint8[] 保存食品流转过程中各个阶段的状态变化
function getTraceInfo(uint256 traceNumber) public constant returns(uint[], string[], address[], uint8[]) {
/* 【12】请在此注释后作答,勿删除此注释*/
require(foods[traceNumber] != address(0), "traceNumber does not exist");
return FoodInfoItem(foods[traceNumber]).getTraceInfo();
}

function getFood(uint256 traceNumber) public constant returns(uint, string, string, string, address, uint8) {
/* 【9】请在此注释后作答,勿删除此注释*/
//补充食品信息获取接口
require(foods[traceNumber] != address(0), "traceNumber does not exist");
return FoodInfoItem(foods[traceNumber]).getFood();

}

function getAllFood() public constant returns (uint[]) {
return foodList;
}
}

这段Solidity代码定义了一个名为Trace的智能合约,该合约继承了ProducerDistributorRetailer三个接口(或合约),并实现了一个食品安全追踪系统。以下是该合约的详细解析:

合约导入和版本声明

1
2
3
4
5
6
pragma solidity >=0.4.22 <0.7.0;
pragma experimental ABIEncoderV2;
import "./FoodInfoItem.sol";
import "./Distributor.sol";
import "./Producer.sol";
import "./Retailer.sol";
  • 声明了合约支持的Solidity版本范围,并启用了ABIEncoderV2实验特性。
  • 导入了FoodInfoItemDistributorProducerRetailer合约,这些合约可能包含了与食品安全追踪相关的接口和功能。

合约定义

1
contract Trace is Producer, Distributor, Retailer {
  • 定义了一个名为Trace的合约,该合约继承了ProducerDistributorRetailer,这意味着Trace合约具有这三个合约的所有功能。

状态变量

1
2
mapping (uint256 => address) foods;
uint[] foodList;
  • foods是一个映射,将食品溯源ID映射到具体的FoodInfoItem合约地址。
  • foodList是一个数组,存储了所有的食品溯源ID。

构造函数

1
2
3
4
5
6
constructor(address producer, address distributor, address retailer)
public
Producer(producer)
Distributor(distributor)
Retailer(retailer) {
}
  • 构造函数接收生产者、分销商和零售商的地址,并调用相应的基础合约构造函数。

生成食品溯源信息接口

1
2
3
4
5
6
function newFood(string name, uint256 traceNumber, string traceName, uint8 quality)
public
onlyProducer
returns(address) {
// ...
}
  • 该函数允许生产者创建新的食品溯源信息,并返回新创建的FoodInfoItem合约地址。

食品分销过程中增加溯源信息的接口

1
2
3
4
5
6
function addTraceInfoByDistributor(uint256 traceNumber, string traceName, uint8 quality)
public
onlyDistributor
returns(bool) {
// ...
}
  • 该函数允许分销商为指定的食品溯源ID添加分销信息。

食品出售过程中增加溯源信息的接口

1
2
3
4
5
6
function addTraceInfoByRetailer(uint256 traceNumber, string traceName, uint8 quality)
public
onlyRetailer
returns(bool) {
// ...
}
  • 该函数允许零售商为指定的食品溯源ID添加零售信息。

获取食品溯源信息接口

1
2
3
4
5
6
function getTraceInfo(uint256 traceNumber)
public
constant
returns(uint[], string[], address[], uint8[]) {
// ...
}
  • 该函数返回指定食品溯源ID的完整追踪信息。

获取食品信息的方法

1
2
3
4
5
6
function getFood(uint256 traceNumber)
public
constant
returns(uint, string, string, string, address, uint8) {
// ...
}
  • 该函数返回指定食品溯源ID的食品初始信息。

获取所有食品溯源ID

1
2
3
function getAllFood() public constant returns (uint[]) {
return foodList;
}
  • 该函数返回所有食品溯源ID的列表。

注意事项

  • 合约中使用了onlyProduceronlyDistributoronlyRetailer这些修饰符,它们应该在继承的合约中定义,以确保只有对应角色的地址能够调用特定的函数。
  • require语句用于验证食品溯源ID的存在性和唯一性。

这个Trace合约通过整合生产者、分销商和零售商的功能,提供了一个完整的食品安全追踪解决方案。它通过区块链技术保证了食品信息的不篡改性,增加了整个食品供应链的透明度。


广州科贸区块链智能合约教程-食品溯源
https://www.zhengcookie.site/2025/07/03/广州科贸区块链智能合约教程-1/
作者
zhengcookie
发布于
2025年7月3日
许可协议