-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathasset_mgmt.go
303 lines (244 loc) · 9.15 KB
/
asset_mgmt.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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.
*/
package main
import (
"encoding/base64"
"errors"
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/crypto/primitives"
"github.com/op/go-logging"
)
var myLogger = logging.MustGetLogger("asset_mgm")
// AssetManagementChaincode is simple chaincode implementing a basic Asset Management system
// with access control enforcement at chaincode level.
// Look here for more information on how to implement access control at chaincode level:
// https://github.com/hyperledger/fabric/blob/master/docs/tech/application-ACL.md
// An asset is simply represented by a string.
type AssetManagementChaincode struct {
}
// Init method will be called during deployment.
// The deploy transaction metadata is supposed to contain the administrator cert
func (t *AssetManagementChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
myLogger.Debug("Init Chaincode...")
if len(args) != 0 {
return nil, errors.New("Incorrect number of arguments. Expecting 0")
}
// Create ownership table
err := stub.CreateTable("AssetsOwnership", []*shim.ColumnDefinition{
&shim.ColumnDefinition{Name: "Asset", Type: shim.ColumnDefinition_STRING, Key: true},
&shim.ColumnDefinition{Name: "Owner", Type: shim.ColumnDefinition_BYTES, Key: false},
})
if err != nil {
return nil, errors.New("Failed creating AssetsOnwership table.")
}
// Set the admin
// The metadata will contain the certificate of the administrator
adminCert, err := stub.GetCallerMetadata()
if err != nil {
myLogger.Debug("Failed getting metadata")
return nil, errors.New("Failed getting metadata.")
}
if len(adminCert) == 0 {
myLogger.Debug("Invalid admin certificate. Empty.")
return nil, errors.New("Invalid admin certificate. Empty.")
}
myLogger.Debug("The administrator is [%x]", adminCert)
stub.PutState("admin", adminCert)
myLogger.Debug("Init Chaincode...done")
return nil, nil
}
func (t *AssetManagementChaincode) assign(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
myLogger.Debug("Assign...")
if len(args) != 2 {
return nil, errors.New("Incorrect number of arguments. Expecting 2")
}
asset := args[0]
owner, err := base64.StdEncoding.DecodeString(args[1])
if err != nil {
return nil, errors.New("Failed decodinf owner")
}
// Verify the identity of the caller
// Only an administrator can invoker assign
adminCertificate, err := stub.GetState("admin")
if err != nil {
return nil, errors.New("Failed fetching admin identity")
}
ok, err := t.isCaller(stub, adminCertificate)
if err != nil {
return nil, errors.New("Failed checking admin identity")
}
if !ok {
return nil, errors.New("The caller is not an administrator")
}
// Register assignment
myLogger.Debugf("New owner of [%s] is [% x]", asset, owner)
ok, err = stub.InsertRow("AssetsOwnership", shim.Row{
Columns: []*shim.Column{
&shim.Column{Value: &shim.Column_String_{String_: asset}},
&shim.Column{Value: &shim.Column_Bytes{Bytes: owner}}},
})
if !ok && err == nil {
return nil, errors.New("Asset was already assigned.")
}
myLogger.Debug("Assign...done!")
return nil, err
}
func (t *AssetManagementChaincode) transfer(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
myLogger.Debug("Transfer...")
if len(args) != 2 {
return nil, errors.New("Incorrect number of arguments. Expecting 2")
}
asset := args[0]
newOwner, err := base64.StdEncoding.DecodeString(args[1])
if err != nil {
return nil, fmt.Errorf("Failed decoding owner")
}
// Verify the identity of the caller
// Only the owner can transfer one of his assets
var columns []shim.Column
col1 := shim.Column{Value: &shim.Column_String_{String_: asset}}
columns = append(columns, col1)
row, err := stub.GetRow("AssetsOwnership", columns)
if err != nil {
return nil, fmt.Errorf("Failed retrieving asset [%s]: [%s]", asset, err)
}
prvOwner := row.Columns[1].GetBytes()
myLogger.Debugf("Previous owener of [%s] is [% x]", asset, prvOwner)
if len(prvOwner) == 0 {
return nil, fmt.Errorf("Invalid previous owner. Nil")
}
// Verify ownership
ok, err := t.isCaller(stub, prvOwner)
if err != nil {
return nil, errors.New("Failed checking asset owner identity")
}
if !ok {
return nil, errors.New("The caller is not the owner of the asset")
}
// At this point, the proof of ownership is valid, then register transfer
err = stub.DeleteRow(
"AssetsOwnership",
[]shim.Column{shim.Column{Value: &shim.Column_String_{String_: asset}}},
)
if err != nil {
return nil, errors.New("Failed deliting row.")
}
_, err = stub.InsertRow(
"AssetsOwnership",
shim.Row{
Columns: []*shim.Column{
&shim.Column{Value: &shim.Column_String_{String_: asset}},
&shim.Column{Value: &shim.Column_Bytes{Bytes: newOwner}},
},
})
if err != nil {
return nil, errors.New("Failed inserting row.")
}
myLogger.Debug("New owner of [%s] is [% x]", asset, newOwner)
myLogger.Debug("Transfer...done")
return nil, nil
}
func (t *AssetManagementChaincode) isCaller(stub shim.ChaincodeStubInterface, certificate []byte) (bool, error) {
myLogger.Debug("Check caller...")
// In order to enforce access control, we require that the
// metadata contains the signature under the signing key corresponding
// to the verification key inside certificate of
// the payload of the transaction (namely, function name and args) and
// the transaction binding (to avoid copying attacks)
// Verify \sigma=Sign(certificate.sk, tx.Payload||tx.Binding) against certificate.vk
// \sigma is in the metadata
sigma, err := stub.GetCallerMetadata()
if err != nil {
return false, errors.New("Failed getting metadata")
}
payload, err := stub.GetPayload()
if err != nil {
return false, errors.New("Failed getting payload")
}
binding, err := stub.GetBinding()
if err != nil {
return false, errors.New("Failed getting binding")
}
myLogger.Debugf("passed certificate [% x]", certificate)
myLogger.Debugf("passed sigma [% x]", sigma)
myLogger.Debugf("passed payload [% x]", payload)
myLogger.Debugf("passed binding [% x]", binding)
ok, err := stub.VerifySignature(
certificate,
sigma,
append(payload, binding...),
)
if err != nil {
myLogger.Errorf("Failed checking signature [%s]", err)
return ok, err
}
if !ok {
myLogger.Error("Invalid signature")
}
myLogger.Debug("Check caller...Verified!")
return ok, err
}
// Invoke will be called for every transaction.
// Supported functions are the following:
// "assign(asset, owner)": to assign ownership of assets. An asset can be owned by a single entity.
// Only an administrator can call this function.
// "transfer(asset, newOwner)": to transfer the ownership of an asset. Only the owner of the specific
// asset can call this function.
// An asset is any string to identify it. An owner is representated by one of his ECert/TCert.
func (t *AssetManagementChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
// Handle different functions
if function == "assign" {
// Assign ownership
return t.assign(stub, args)
} else if function == "transfer" {
// Transfer ownership
return t.transfer(stub, args)
}
return nil, errors.New("Received unknown function invocation")
}
// Query callback representing the query of a chaincode
// Supported functions are the following:
// "query(asset)": returns the owner of the asset.
// Anyone can invoke this function.
func (t *AssetManagementChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
myLogger.Debugf("Query [%s]", function)
if function != "query" {
return nil, errors.New("Invalid query function name. Expecting 'query' but found '" + function + "'")
}
var err error
if len(args) != 1 {
myLogger.Debug("Incorrect number of arguments. Expecting name of an asset to query")
return nil, errors.New("Incorrect number of arguments. Expecting name of an asset to query")
}
// Who is the owner of the asset?
asset := args[0]
myLogger.Debugf("Arg [%s]", string(asset))
var columns []shim.Column
col1 := shim.Column{Value: &shim.Column_String_{String_: asset}}
columns = append(columns, col1)
row, err := stub.GetRow("AssetsOwnership", columns)
if err != nil {
myLogger.Debugf("Failed retriving asset [%s]: [%s]", string(asset), err)
return nil, fmt.Errorf("Failed retriving asset [%s]: [%s]", string(asset), err)
}
myLogger.Debugf("Query done [% x]", row.Columns[1].GetBytes())
return row.Columns[1].GetBytes(), nil
}
func main() {
primitives.SetSecurityLevel("SHA3", 256)
err := shim.Start(new(AssetManagementChaincode))
if err != nil {
fmt.Printf("Error starting AssetManagementChaincode: %s", err)
}
}