开发智能合约

开发智能合约

本次的智能合约从fabric-samplesasset-transfer-basic/chaincode-go拷贝然后进行了修改。

智能合约代码smartcontract.go如下:本文档更新不及时,可到源码地址查看。

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package chaincode

import (
"encoding/json"
"fmt"
"time"

"github.com/golang/protobuf/ptypes"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// SmartContract provides functions for managing an Asset
type SmartContract struct {
contractapi.Contract
}

// Asset describes basic details of what makes up a simple asset
type Asset struct {
ID string `json:"ID"`
Issuer string `json:"issuer"`
Owner string `json:"owner"`
Amount int64 `json:"amount"`
CreateDate time.Time `json:"createDate"`
EndDate time.Time `json:"endDate"`
ContractHash string `json:"contractHash"`
InvoiceHash string `json:"invoiceHash"`
}

// IssueVoucher 发行凭证
func (s *SmartContract) IssueVoucher(ctx contractapi.TransactionContextInterface, assetID string, amount int64, owner string, contractHash string, invoiceHash string) error {
// 创建资产
return s.CreateAssetAndSave(ctx, assetID, amount, "核心企业", owner, contractHash, invoiceHash)
}

// CreateAssetAndSave 创建资产并保存
func (s *SmartContract) CreateAssetAndSave(ctx contractapi.TransactionContextInterface, id string, amount int64, issuerName string, owner string, contractHash string, invoiceHash string) error {
asset, err := s.createAsset(ctx, id, amount, issuerName, owner, contractHash, invoiceHash)
if err != nil {
return err
}
return s.PutState(ctx, asset)
}

// createAsset issues a new asset to the world state with given details.
func (s *SmartContract) createAsset(ctx contractapi.TransactionContextInterface, id string, amount int64, issuerName string, owner string, contractHash string, invoiceHash string) (*Asset, error) {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return nil, err
}
if exists {
return nil, fmt.Errorf("the asset %s already exists", id)
}
now, err := getNow(ctx)
if err != nil {
return nil, err
}
asset := Asset{ID: id, Issuer: issuerName, Amount: amount, Owner: owner, CreateDate: now,
EndDate: now.AddDate(0, 6, 0), ContractHash: contractHash, InvoiceHash: invoiceHash}
return &asset, nil
}

// 获取当前时间
func getNow(ctx contractapi.TransactionContextInterface) (time.Time, error) {
now, err := ctx.GetStub().GetTxTimestamp()
if err != nil {
return time.Now(), err
}
return ptypes.Timestamp(now)
}

// ReadAsset returns the asset stored in the world state with given id.
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if assetJSON == nil {
return nil, fmt.Errorf("the asset %s does not exist", id)
}

var asset Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return nil, err
}

return &asset, nil
}

// AssetExists returns true when asset with given ID exists in world state
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}

return assetJSON != nil, nil
}

// TransferAssetByID 根据资产ID转账
func (s *SmartContract) TransferAssetByID(ctx contractapi.TransactionContextInterface, id string, newID string, newOwner string, amount int64, contractHash string, invoiceHash string) error {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return err
}
return s.TransferAsset(ctx, asset, newID, newOwner, amount, contractHash, invoiceHash)
}

// TransferAsset updates the owner field of asset with given id in world state.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, asset *Asset, newID string, newOwner string, amount int64, contractHash string, invoiceHash string) error {
// 如果金额刚好等于凭证资产的金额,直接更新凭证资产的拥有者
if asset.Amount == amount {
asset.Owner = newOwner
return s.PutState(ctx, asset)
} else if asset.Amount > amount { // 如果凭证资产的金额大于转账的金额,则创建一个新的资产
if newID == "" {
return fmt.Errorf("转账金额小于资产的金额时,newID必须不能为空")
}
// 创建新的资产并保存
err := s.CreateAssetAndSave(ctx, newID, amount, asset.Issuer, newOwner, contractHash, invoiceHash)
if err != nil {
return err
}
// 更新旧资产的金额
asset.Amount = asset.Amount - amount
return s.PutState(ctx, asset)
} else if asset.Amount < amount { // 如果资产的额度小于要转账的金额,则直接报错
return fmt.Errorf("转账金额=%d,不能超过资产的金额=%d", amount, asset.Amount)
}
return nil
}

// PutState 更新资产
func (s *SmartContract) PutState(ctx contractapi.TransactionContextInterface, asset *Asset) error {
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(asset.ID, assetJSON)
}

// DelState 删除资产
func (s *SmartContract) DelState(ctx contractapi.TransactionContextInterface, assetID string) error {
exists, err := s.AssetExists(ctx, assetID)
if err != nil {
return nil
}
if !exists {
return fmt.Errorf("assetID=%s的资产不存在", assetID)
}
return ctx.GetStub().DelState(assetID)
}

// GetAllAssets returns all assets found in world state
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
// range query with empty string for startKey and endKey does an
// open-ended query of all assets in the chaincode namespace.
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
if err != nil {
return nil, err
}
defer resultsIterator.Close()

var assets []*Asset
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}

var asset Asset
err = json.Unmarshal(queryResponse.Value, &asset)
if err != nil {
return nil, err
}
assets = append(assets, &asset)
}

return assets, nil
}