mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-22 17:45:10 +00:00
[FAB-4371] - Chaincode Dev Mode
Adds a docker environment for running peer in dev mode pre-baked crypto and orderer/channel artifacts README still needs an updated curl command to bundle these scripts. simplified some of the paths add sacc from write your first chaincode remove binary Change-Id: Ic1537987c819f0350ae6dbf740fd00e1571045e5 Signed-off-by: Nick Gaski <ngaski@us.ibm.com> Signed-off-by: Christopher Ferris <chrisfer@us.ibm.com>
This commit is contained in:
parent
ec254d983c
commit
ba777f3bcb
14 changed files with 724 additions and 0 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -11,3 +11,6 @@ first-network/channel-artifacts/*.tx
|
||||||
first-network/channel-artifacts/*.block
|
first-network/channel-artifacts/*.block
|
||||||
first-network/crypto-config/*
|
first-network/crypto-config/*
|
||||||
|
|
||||||
|
chaincode-docker-devmode/myc.block
|
||||||
|
chaincode-docker-devmode/chaincode/sacc/sacc
|
||||||
|
chaincode-docker-devmode/chaincode/chaincode_example02/chaincode_example02
|
||||||
|
|
|
||||||
140
chaincode-docker-devmode/README.rst
Normal file
140
chaincode-docker-devmode/README.rst
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
Using dev mode
|
||||||
|
==============
|
||||||
|
|
||||||
|
Normally chaincodes are started and maintained by peer. However in “dev
|
||||||
|
mode", chaincode is built and started by the user. This mode is useful
|
||||||
|
during chaincode development phase for rapid code/build/run/debug cycle
|
||||||
|
turnaround.
|
||||||
|
|
||||||
|
We start "dev mode" by leveraging pre-generated orderer and channel artifacts for
|
||||||
|
a sample dev network. As such, the user can immediately jump into the process
|
||||||
|
of compiling chaincode and driving calls.
|
||||||
|
|
||||||
|
Install Fabric Samples
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
If you haven't already done so, please install the :doc:`samples`.
|
||||||
|
|
||||||
|
Navigate to the ``chaincode-docker-devmode`` directory of the ``fabric-samples``
|
||||||
|
clone:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
cd chaincode-docker-devmode
|
||||||
|
|
||||||
|
Download docker images
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
We need four docker images in order for "dev mode" to run against the supplied
|
||||||
|
docker compose script. If you installed the ``fabric-samples`` repo clone and
|
||||||
|
followed the instructions to :ref:`download-platform-specific-binaries`, then
|
||||||
|
you should have the necessary Docker images installed locally.
|
||||||
|
|
||||||
|
.. note:: If you choose to manually pull the images then you must retag them as
|
||||||
|
``latest``.
|
||||||
|
|
||||||
|
Issue a ``docker images`` command to reveal your local Docker Registry. You
|
||||||
|
should see something similar to following:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
docker images
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||||
|
hyperledger/fabric-tools latest e09f38f8928d 4 hours ago 1.32 GB
|
||||||
|
hyperledger/fabric-tools x86_64-1.0.0-rc1-snapshot-f20846c6 e09f38f8928d 4 hours ago 1.32 GB
|
||||||
|
hyperledger/fabric-orderer latest 0df93ba35a25 4 hours ago 179 MB
|
||||||
|
hyperledger/fabric-orderer x86_64-1.0.0-rc1-snapshot-f20846c6 0df93ba35a25 4 hours ago 179 MB
|
||||||
|
hyperledger/fabric-peer latest 533aec3f5a01 4 hours ago 182 MB
|
||||||
|
hyperledger/fabric-peer x86_64-1.0.0-rc1-snapshot-f20846c6 533aec3f5a01 4 hours ago 182 MB
|
||||||
|
hyperledger/fabric-ccenv latest 4b70698a71d3 4 hours ago 1.29 GB
|
||||||
|
hyperledger/fabric-ccenv x86_64-1.0.0-rc1-snapshot-f20846c6 4b70698a71d3 4 hours ago 1.29 GB
|
||||||
|
|
||||||
|
.. note:: If you retrieved the images through the :ref:`download-platform-specific-binaries`,
|
||||||
|
then you will see additional images listed. However, we are only concerned with
|
||||||
|
these four.
|
||||||
|
|
||||||
|
Now open three terminals and navigate to your ``chaincode-docker-devmode``
|
||||||
|
directory in each.
|
||||||
|
|
||||||
|
Terminal 1 - Start the network
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
docker-compose -f docker-compose-simple.yaml up
|
||||||
|
|
||||||
|
The above starts the network with the ``SingleSampleMSPSolo`` orderer profile and
|
||||||
|
launches the peer in "dev mode". It also launches two additional containers -
|
||||||
|
one for the chaincode environment and a CLI to interact with the chaincode. The
|
||||||
|
commands for create and join channel are embedded in the CLI container, so we
|
||||||
|
can jump immediately to the chaincode calls.
|
||||||
|
|
||||||
|
Terminal 2 - Build & start the chaincode
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
docker exec -it chaincode bash
|
||||||
|
|
||||||
|
You should see the following:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
root@d2629980e76b:/opt/gopath/src/chaincode#
|
||||||
|
|
||||||
|
Now, compile your chaincode:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
cd chaincode_example02
|
||||||
|
go build
|
||||||
|
|
||||||
|
Now run the chaincode:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./chaincode_example02
|
||||||
|
|
||||||
|
The chaincode is started with peer and chaincode logs indicating successful registration with the peer.
|
||||||
|
Note that at this stage the chaincode is not associated with any channel. This is done in subsequent steps
|
||||||
|
using the ``instantiate`` command.
|
||||||
|
|
||||||
|
Terminal 3 - Use the chaincode
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Even though you are in ``--peer-chaincodedev`` mode, you still have to install the
|
||||||
|
chaincode so the life-cycle system chaincode can go through its checks normally.
|
||||||
|
This requirement may be removed in future when in ``--peer-chaincodedev`` mode.
|
||||||
|
|
||||||
|
We'll leverage the CLI container to drive these calls.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
docker exec -it cli bash
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
peer chaincode install -p chaincodedev/chaincode/chaincode_example02 -n mycc -v 0
|
||||||
|
peer chaincode instantiate -n mycc -v 0 -c '{"Args":["init","a","100","b","200"]}' -C myc
|
||||||
|
|
||||||
|
Now issue an invoke to move ``10`` from ``a`` to ``b``.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
peer chaincode invoke -n mycc -c '{"Args":["invoke","a","b","10"]}' -C myc
|
||||||
|
|
||||||
|
Finally, query ``a``. We should see a value of ``90``.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
|
||||||
|
|
||||||
|
Testing new chaincode
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
By default, we mount only ``chaincode_example02``. However, you can easily test different
|
||||||
|
chaincodes by adding them to the ``chaincode`` subdirectory and relaunching
|
||||||
|
your network. At this point they will be accessible in your ``chaincode`` container.
|
||||||
|
|
||||||
|
.. Licensed under Creative Commons Attribution 4.0 International License
|
||||||
|
https://creativecommons.org/licenses/by/4.0/
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of
|
||||||
|
//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has
|
||||||
|
//to be modified as well with the new ID of chaincode_example02.
|
||||||
|
//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of
|
||||||
|
//hard-coding.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric/core/chaincode/shim"
|
||||||
|
pb "github.com/hyperledger/fabric/protos/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SimpleChaincode example simple Chaincode implementation
|
||||||
|
type SimpleChaincode struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
|
||||||
|
fmt.Println("ex02 Init")
|
||||||
|
_, args := stub.GetFunctionAndParameters()
|
||||||
|
var A, B string // Entities
|
||||||
|
var Aval, Bval int // Asset holdings
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(args) != 4 {
|
||||||
|
return shim.Error("Incorrect number of arguments. Expecting 4")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the chaincode
|
||||||
|
A = args[0]
|
||||||
|
Aval, err = strconv.Atoi(args[1])
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error("Expecting integer value for asset holding")
|
||||||
|
}
|
||||||
|
B = args[2]
|
||||||
|
Bval, err = strconv.Atoi(args[3])
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error("Expecting integer value for asset holding")
|
||||||
|
}
|
||||||
|
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
|
||||||
|
|
||||||
|
// Write the state to the ledger
|
||||||
|
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return shim.Success(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
|
||||||
|
fmt.Println("ex02 Invoke")
|
||||||
|
function, args := stub.GetFunctionAndParameters()
|
||||||
|
if function == "invoke" {
|
||||||
|
// Make payment of X units from A to B
|
||||||
|
return t.invoke(stub, args)
|
||||||
|
} else if function == "delete" {
|
||||||
|
// Deletes an entity from its state
|
||||||
|
return t.delete(stub, args)
|
||||||
|
} else if function == "query" {
|
||||||
|
// the old "Query" is now implemtned in invoke
|
||||||
|
return t.query(stub, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction makes payment of X units from A to B
|
||||||
|
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
||||||
|
var A, B string // Entities
|
||||||
|
var Aval, Bval int // Asset holdings
|
||||||
|
var X int // Transaction value
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(args) != 3 {
|
||||||
|
return shim.Error("Incorrect number of arguments. Expecting 3")
|
||||||
|
}
|
||||||
|
|
||||||
|
A = args[0]
|
||||||
|
B = args[1]
|
||||||
|
|
||||||
|
// Get the state from the ledger
|
||||||
|
// TODO: will be nice to have a GetAllState call to ledger
|
||||||
|
Avalbytes, err := stub.GetState(A)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error("Failed to get state")
|
||||||
|
}
|
||||||
|
if Avalbytes == nil {
|
||||||
|
return shim.Error("Entity not found")
|
||||||
|
}
|
||||||
|
Aval, _ = strconv.Atoi(string(Avalbytes))
|
||||||
|
|
||||||
|
Bvalbytes, err := stub.GetState(B)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error("Failed to get state")
|
||||||
|
}
|
||||||
|
if Bvalbytes == nil {
|
||||||
|
return shim.Error("Entity not found")
|
||||||
|
}
|
||||||
|
Bval, _ = strconv.Atoi(string(Bvalbytes))
|
||||||
|
|
||||||
|
// Perform the execution
|
||||||
|
X, err = strconv.Atoi(args[2])
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error("Invalid transaction amount, expecting a integer value")
|
||||||
|
}
|
||||||
|
Aval = Aval - X
|
||||||
|
Bval = Bval + X
|
||||||
|
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
|
||||||
|
|
||||||
|
// Write the state back to the ledger
|
||||||
|
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return shim.Success(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes an entity from state
|
||||||
|
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return shim.Error("Incorrect number of arguments. Expecting 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
A := args[0]
|
||||||
|
|
||||||
|
// Delete the key from the state in ledger
|
||||||
|
err := stub.DelState(A)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error("Failed to delete state")
|
||||||
|
}
|
||||||
|
|
||||||
|
return shim.Success(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// query callback representing the query of a chaincode
|
||||||
|
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
|
||||||
|
var A string // Entities
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(args) != 1 {
|
||||||
|
return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
|
||||||
|
}
|
||||||
|
|
||||||
|
A = args[0]
|
||||||
|
|
||||||
|
// Get the state from the ledger
|
||||||
|
Avalbytes, err := stub.GetState(A)
|
||||||
|
if err != nil {
|
||||||
|
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
|
||||||
|
return shim.Error(jsonResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Avalbytes == nil {
|
||||||
|
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
|
||||||
|
return shim.Error(jsonResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
|
||||||
|
fmt.Printf("Query Response:%s\n", jsonResp)
|
||||||
|
return shim.Success(Avalbytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := shim.Start(new(SimpleChaincode))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error starting Simple chaincode: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric/core/chaincode/shim"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkInit(t *testing.T, stub *shim.MockStub, args [][]byte) {
|
||||||
|
res := stub.MockInit("1", args)
|
||||||
|
if res.Status != shim.OK {
|
||||||
|
fmt.Println("Init failed", string(res.Message))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkState(t *testing.T, stub *shim.MockStub, name string, value string) {
|
||||||
|
bytes := stub.State[name]
|
||||||
|
if bytes == nil {
|
||||||
|
fmt.Println("State", name, "failed to get value")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if string(bytes) != value {
|
||||||
|
fmt.Println("State value", name, "was not", value, "as expected")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkQuery(t *testing.T, stub *shim.MockStub, name string, value string) {
|
||||||
|
res := stub.MockInvoke("1", [][]byte{[]byte("query"), []byte(name)})
|
||||||
|
if res.Status != shim.OK {
|
||||||
|
fmt.Println("Query", name, "failed", string(res.Message))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if res.Payload == nil {
|
||||||
|
fmt.Println("Query", name, "failed to get value")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if string(res.Payload) != value {
|
||||||
|
fmt.Println("Query value", name, "was not", value, "as expected")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkInvoke(t *testing.T, stub *shim.MockStub, args [][]byte) {
|
||||||
|
res := stub.MockInvoke("1", args)
|
||||||
|
if res.Status != shim.OK {
|
||||||
|
fmt.Println("Invoke", args, "failed", string(res.Message))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExample02_Init(t *testing.T) {
|
||||||
|
scc := new(SimpleChaincode)
|
||||||
|
stub := shim.NewMockStub("ex02", scc)
|
||||||
|
|
||||||
|
// Init A=123 B=234
|
||||||
|
checkInit(t, stub, [][]byte{[]byte("init"), []byte("A"), []byte("123"), []byte("B"), []byte("234")})
|
||||||
|
|
||||||
|
checkState(t, stub, "A", "123")
|
||||||
|
checkState(t, stub, "B", "234")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExample02_Query(t *testing.T) {
|
||||||
|
scc := new(SimpleChaincode)
|
||||||
|
stub := shim.NewMockStub("ex02", scc)
|
||||||
|
|
||||||
|
// Init A=345 B=456
|
||||||
|
checkInit(t, stub, [][]byte{[]byte("init"), []byte("A"), []byte("345"), []byte("B"), []byte("456")})
|
||||||
|
|
||||||
|
// Query A
|
||||||
|
checkQuery(t, stub, "A", "345")
|
||||||
|
|
||||||
|
// Query B
|
||||||
|
checkQuery(t, stub, "B", "456")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExample02_Invoke(t *testing.T) {
|
||||||
|
scc := new(SimpleChaincode)
|
||||||
|
stub := shim.NewMockStub("ex02", scc)
|
||||||
|
|
||||||
|
// Init A=567 B=678
|
||||||
|
checkInit(t, stub, [][]byte{[]byte("init"), []byte("A"), []byte("567"), []byte("B"), []byte("678")})
|
||||||
|
|
||||||
|
// Invoke A->B for 123
|
||||||
|
checkInvoke(t, stub, [][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("123")})
|
||||||
|
checkQuery(t, stub, "A", "444")
|
||||||
|
checkQuery(t, stub, "B", "801")
|
||||||
|
|
||||||
|
// Invoke B->A for 234
|
||||||
|
checkInvoke(t, stub, [][]byte{[]byte("invoke"), []byte("B"), []byte("A"), []byte("234")})
|
||||||
|
checkQuery(t, stub, "A", "678")
|
||||||
|
checkQuery(t, stub, "B", "567")
|
||||||
|
checkQuery(t, stub, "A", "678")
|
||||||
|
checkQuery(t, stub, "B", "567")
|
||||||
|
}
|
||||||
97
chaincode-docker-devmode/chaincode/sacc/sacc.go
Normal file
97
chaincode-docker-devmode/chaincode/sacc/sacc.go
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp All Rights Reserved
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric/core/chaincode/shim"
|
||||||
|
"github.com/hyperledger/fabric/protos/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SimpleAsset implements a simple chaincode to manage an asset
|
||||||
|
type SimpleAsset struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init is called during chaincode instantiation to initialize any
|
||||||
|
// data. Note that chaincode upgrade also calls this function to reset
|
||||||
|
// or to migrate data.
|
||||||
|
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
|
||||||
|
// Get the args from the transaction proposal
|
||||||
|
args := stub.GetStringArgs()
|
||||||
|
if len(args) != 2 {
|
||||||
|
return shim.Error("Incorrect arguments. Expecting a key and a value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up any variables or assets here by calling stub.PutState()
|
||||||
|
|
||||||
|
// We store the key and the value on the ledger
|
||||||
|
err := stub.PutState(args[0], []byte(args[1]))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
|
||||||
|
}
|
||||||
|
return shim.Success(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke is called per transaction on the chaincode. Each transaction is
|
||||||
|
// either a 'get' or a 'set' on the asset created by Init function. The Set
|
||||||
|
// method may create a new asset by specifying a new key-value pair.
|
||||||
|
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
|
||||||
|
// Extract the function and args from the transaction proposal
|
||||||
|
fn, args := stub.GetFunctionAndParameters()
|
||||||
|
|
||||||
|
var result string
|
||||||
|
var err error
|
||||||
|
if fn == "set" {
|
||||||
|
result, err = set(stub, args)
|
||||||
|
} else { // assume 'get' even if fn is nil
|
||||||
|
result, err = get(stub, args)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the result as success payload
|
||||||
|
return shim.Success([]byte(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stores the asset (both key and value) on the ledger. If the key exists,
|
||||||
|
// it will override the value with the new one
|
||||||
|
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := stub.PutState(args[0], []byte(args[1]))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to set asset: %s", args[0])
|
||||||
|
}
|
||||||
|
return args[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value of the specified asset key
|
||||||
|
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := stub.GetState(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
|
||||||
|
}
|
||||||
|
if value == nil {
|
||||||
|
return "", fmt.Errorf("Asset not found: %s", args[0])
|
||||||
|
}
|
||||||
|
return string(value), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// main function starts up the chaincode in the container during instantiate
|
||||||
|
func main() {
|
||||||
|
if err := shim.Start(new(SimpleAsset)); err != nil {
|
||||||
|
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
87
chaincode-docker-devmode/docker-compose-simple.yaml
Normal file
87
chaincode-docker-devmode/docker-compose-simple.yaml
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
orderer:
|
||||||
|
container_name: orderer
|
||||||
|
image: hyperledger/fabric-orderer
|
||||||
|
environment:
|
||||||
|
- ORDERER_GENERAL_LOGLEVEL=debug
|
||||||
|
- ORDERER_GENERAL_LISTENADDRESS=orderer
|
||||||
|
- ORDERER_GENERAL_GENESISMETHOD=file
|
||||||
|
- ORDERER_GENERAL_GENESISFILE=orderer.block
|
||||||
|
- ORDERER_GENERAL_LOCALMSPID=DEFAULT
|
||||||
|
- ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/msp
|
||||||
|
- GRPC_TRACE=all=true,
|
||||||
|
- GRPC_VERBOSITY=debug
|
||||||
|
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
|
||||||
|
command: orderer
|
||||||
|
volumes:
|
||||||
|
- ./msp:/etc/hyperledger/msp
|
||||||
|
- ./orderer.block:/etc/hyperledger/fabric/orderer.block
|
||||||
|
ports:
|
||||||
|
- 7050:7050
|
||||||
|
peer:
|
||||||
|
container_name: peer
|
||||||
|
image: hyperledger/fabric-peer
|
||||||
|
environment:
|
||||||
|
- CORE_PEER_ID=peer
|
||||||
|
- CORE_PEER_ADDRESS=peer:7051
|
||||||
|
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=DEFAULT
|
||||||
|
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
|
||||||
|
- CORE_LOGGING_LEVEL=DEBUG
|
||||||
|
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp
|
||||||
|
volumes:
|
||||||
|
- /var/run/:/host/var/run/
|
||||||
|
- ./msp:/etc/hyperledger/msp
|
||||||
|
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
|
||||||
|
command: peer node start --peer-chaincodedev=true -o orderer:7050
|
||||||
|
ports:
|
||||||
|
- 7051:7051
|
||||||
|
- 7053:7053
|
||||||
|
depends_on:
|
||||||
|
- orderer
|
||||||
|
|
||||||
|
cli:
|
||||||
|
container_name: cli
|
||||||
|
image: hyperledger/fabric-tools
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
- GOPATH=/opt/gopath
|
||||||
|
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
|
||||||
|
- CORE_LOGGING_LEVEL=DEBUG
|
||||||
|
- CORE_PEER_ID=cli
|
||||||
|
- CORE_PEER_ADDRESS=peer:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=DEFAULT
|
||||||
|
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp
|
||||||
|
working_dir: /opt/gopath/src/chaincodedev
|
||||||
|
command: /bin/bash -c './script.sh'
|
||||||
|
volumes:
|
||||||
|
- /var/run/:/host/var/run/
|
||||||
|
- ./msp:/etc/hyperledger/msp
|
||||||
|
- ./:/opt/gopath/src/chaincodedev
|
||||||
|
depends_on:
|
||||||
|
- orderer
|
||||||
|
- peer
|
||||||
|
|
||||||
|
chaincode:
|
||||||
|
container_name: chaincode
|
||||||
|
image: hyperledger/fabric-ccenv
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
- GOPATH=/opt/gopath
|
||||||
|
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
|
||||||
|
- CORE_LOGGING_LEVEL=DEBUG
|
||||||
|
- CORE_PEER_ID=example02
|
||||||
|
- CORE_PEER_ADDRESS=peer:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=DEFAULT
|
||||||
|
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp
|
||||||
|
working_dir: /opt/gopath/src/chaincode
|
||||||
|
command: /bin/bash -c 'sleep 6000000'
|
||||||
|
volumes:
|
||||||
|
- /var/run/:/host/var/run/
|
||||||
|
- ./msp:/etc/hyperledger/msp
|
||||||
|
- ./chaincode:/opt/gopath/src/chaincode
|
||||||
|
depends_on:
|
||||||
|
- orderer
|
||||||
|
- peer
|
||||||
16
chaincode-docker-devmode/msp/admincerts/admincert.pem
Normal file
16
chaincode-docker-devmode/msp/admincerts/admincert.pem
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICjDCCAjKgAwIBAgIUBEVwsSx0TmqdbzNwleNBBzoIT0wwCgYIKoZIzj0EAwIw
|
||||||
|
fzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh
|
||||||
|
biBGcmFuY2lzY28xHzAdBgNVBAoTFkludGVybmV0IFdpZGdldHMsIEluYy4xDDAK
|
||||||
|
BgNVBAsTA1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYxMTExMTcwNzAw
|
||||||
|
WhcNMTcxMTExMTcwNzAwWjBjMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGgg
|
||||||
|
Q2Fyb2xpbmExEDAOBgNVBAcTB1JhbGVpZ2gxGzAZBgNVBAoTEkh5cGVybGVkZ2Vy
|
||||||
|
IEZhYnJpYzEMMAoGA1UECxMDQ09QMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||||
|
HBuKsAO43hs4JGpFfiGMkB/xsILTsOvmN2WmwpsPHZNL6w8HWe3xCPQtdG/XJJvZ
|
||||||
|
+C756KEsUBM3yw5PTfku8qOBpzCBpDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
|
||||||
|
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOFC
|
||||||
|
dcUZ4es3ltiCgAVDoyLfVpPIMB8GA1UdIwQYMBaAFBdnQj2qnoI/xMUdn1vDmdG1
|
||||||
|
nEgQMCUGA1UdEQQeMByCCm15aG9zdC5jb22CDnd3dy5teWhvc3QuY29tMAoGCCqG
|
||||||
|
SM49BAMCA0gAMEUCIDf9Hbl4xn3z4EwNKmilM9lX2Fq4jWpAaRVB97OmVEeyAiEA
|
||||||
|
25aDPQHGGq2AvhKT0wvt08cX1GTGCIbfmuLpMwKQj38=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
15
chaincode-docker-devmode/msp/cacerts/cacert.pem
Normal file
15
chaincode-docker-devmode/msp/cacerts/cacert.pem
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICYjCCAgmgAwIBAgIUB3CTDOU47sUC5K4kn/Caqnh114YwCgYIKoZIzj0EAwIw
|
||||||
|
fzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh
|
||||||
|
biBGcmFuY2lzY28xHzAdBgNVBAoTFkludGVybmV0IFdpZGdldHMsIEluYy4xDDAK
|
||||||
|
BgNVBAsTA1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYxMDEyMTkzMTAw
|
||||||
|
WhcNMjExMDExMTkzMTAwWjB/MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv
|
||||||
|
cm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEfMB0GA1UEChMWSW50ZXJuZXQg
|
||||||
|
V2lkZ2V0cywgSW5jLjEMMAoGA1UECxMDV1dXMRQwEgYDVQQDEwtleGFtcGxlLmNv
|
||||||
|
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKIH5b2JaSmqiQXHyqC+cmknICcF
|
||||||
|
i5AddVjsQizDV6uZ4v6s+PWiJyzfA/rTtMvYAPq/yeEHpBUB1j053mxnpMujYzBh
|
||||||
|
MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQXZ0I9
|
||||||
|
qp6CP8TFHZ9bw5nRtZxIEDAfBgNVHSMEGDAWgBQXZ0I9qp6CP8TFHZ9bw5nRtZxI
|
||||||
|
EDAKBggqhkjOPQQDAgNHADBEAiAHp5Rbp9Em1G/UmKn8WsCbqDfWecVbZPQj3RK4
|
||||||
|
oG5kQQIgQAe4OOKYhJdh3f7URaKfGTf492/nmRmtK+ySKjpHSrU=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
8
chaincode-docker-devmode/msp/config.yaml
Normal file
8
chaincode-docker-devmode/msp/config.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright IBM Corp. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
OrganizationalUnitIdentifiers:
|
||||||
|
- Certificate: "cacerts/cacert.pem"
|
||||||
|
OrganizationalUnitIdentifier: "COP"
|
||||||
5
chaincode-docker-devmode/msp/keystore/key.pem
Normal file
5
chaincode-docker-devmode/msp/keystore/key.pem
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIAsWwFunEzqz1Rh6nvD4MiPkKCtmoxzh3jTquG5MSbeLoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEHBuKsAO43hs4JGpFfiGMkB/xsILTsOvmN2WmwpsPHZNL6w8HWe3x
|
||||||
|
CPQtdG/XJJvZ+C756KEsUBM3yw5PTfku8g==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
16
chaincode-docker-devmode/msp/signcerts/peer.pem
Normal file
16
chaincode-docker-devmode/msp/signcerts/peer.pem
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICjDCCAjKgAwIBAgIUBEVwsSx0TmqdbzNwleNBBzoIT0wwCgYIKoZIzj0EAwIw
|
||||||
|
fzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh
|
||||||
|
biBGcmFuY2lzY28xHzAdBgNVBAoTFkludGVybmV0IFdpZGdldHMsIEluYy4xDDAK
|
||||||
|
BgNVBAsTA1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYxMTExMTcwNzAw
|
||||||
|
WhcNMTcxMTExMTcwNzAwWjBjMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGgg
|
||||||
|
Q2Fyb2xpbmExEDAOBgNVBAcTB1JhbGVpZ2gxGzAZBgNVBAoTEkh5cGVybGVkZ2Vy
|
||||||
|
IEZhYnJpYzEMMAoGA1UECxMDQ09QMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||||
|
HBuKsAO43hs4JGpFfiGMkB/xsILTsOvmN2WmwpsPHZNL6w8HWe3xCPQtdG/XJJvZ
|
||||||
|
+C756KEsUBM3yw5PTfku8qOBpzCBpDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
|
||||||
|
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOFC
|
||||||
|
dcUZ4es3ltiCgAVDoyLfVpPIMB8GA1UdIwQYMBaAFBdnQj2qnoI/xMUdn1vDmdG1
|
||||||
|
nEgQMCUGA1UdEQQeMByCCm15aG9zdC5jb22CDnd3dy5teWhvc3QuY29tMAoGCCqG
|
||||||
|
SM49BAMCA0gAMEUCIDf9Hbl4xn3z4EwNKmilM9lX2Fq4jWpAaRVB97OmVEeyAiEA
|
||||||
|
25aDPQHGGq2AvhKT0wvt08cX1GTGCIbfmuLpMwKQj38=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
BIN
chaincode-docker-devmode/myc.tx
Normal file
BIN
chaincode-docker-devmode/myc.tx
Normal file
Binary file not shown.
BIN
chaincode-docker-devmode/orderer.block
Normal file
BIN
chaincode-docker-devmode/orderer.block
Normal file
Binary file not shown.
26
chaincode-docker-devmode/script.sh
Executable file
26
chaincode-docker-devmode/script.sh
Executable file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright London Stock Exchange Group All Rights Reserved.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
set -e
|
||||||
|
# This script expedites the chaincode development process by automating the
|
||||||
|
# requisite channel create/join commands
|
||||||
|
|
||||||
|
# We use a pre-generated orderer.block and channel transaction artifact (myc.tx),
|
||||||
|
# both of which are created using the configtxgen tool
|
||||||
|
|
||||||
|
# first we create the channel against the specified configuration in myc.tx
|
||||||
|
# this call returns a channel configuration block - myc.block - to the CLI container
|
||||||
|
peer channel create -c myc -f myc.tx -o orderer:7050
|
||||||
|
|
||||||
|
# now we will join the channel and start the chain with myc.block serving as the
|
||||||
|
# channel's first block (i.e. the genesis block)
|
||||||
|
peer channel join -b myc.block
|
||||||
|
|
||||||
|
# Now the user can proceed to build and start chaincode in one terminal
|
||||||
|
# And leverage the CLI container to issue install instantiate invoke query commands in another
|
||||||
|
|
||||||
|
#we should have bailed if above commands failed.
|
||||||
|
#we are here, so they worked
|
||||||
|
sleep 600000
|
||||||
|
exit 0
|
||||||
Loading…
Reference in a new issue