需求分析
1)初始两个用户,赋予一定金额。
2)转账
3)查询余额
4)删除账户
go语言版
项目结构
项目代码
package main
import (
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
var logger = shim.NewLogger("example_cc0")
// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}
// Init initializes the chaincode state
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
logger.Info("########### example_cc 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")
}
logger.Infof("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())
}
if transientMap, err := stub.GetTransient(); err == nil {
rc := transientMap["rc"]
transientData := transientMap["result"]
if rc == nil {
return shim.Success(transientData)
}
vrc, err := strconv.Atoi(string(rc[:]))
if err != nil {
return shim.Error(err.Error())
}
return pb.Response{
Status: int32(vrc),
Payload: transientData,
}
}
return shim.Success(nil)
}
// Invoke makes payment of X units from A to B
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
logger.Info("########### example_cc Invoke ###########")
function, args := stub.GetFunctionAndParameters()
if function == "delete" {
// Deletes an entity from its state
return t.delete(stub, args)
}
if function == "move" {
// Deletes an entity from its state
return t.move(stub, args)
}
if function == "query" {
return t.query(stub, args)
}
logger.Errorf("Unknown action, check the first argument, must be one of 'delete', 'query', or 'move'. But got: %v", args[0])
return shim.Error(fmt.Sprintf("Unknown action, check the first argument, must be one of 'delete', 'query', or 'move'. But got: %v", args[0]))
}
func (t *SimpleChaincode) move(stub shim.ChaincodeStubInterface, args []string) pb.Response {
// must be an invoke
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, function followed by 2 names and 1 value")
}
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
logger.Infof("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())
}
if transientMap, err := stub.GetTransient(); err == nil {
if transientData, ok := transientMap["event"]; ok {
stub.SetEvent("event", transientData)
}
rc := transientMap["rc"]
transientData := transientMap["result"]
if rc == nil {
return shim.Success(transientData)
}
vrc, err := strconv.Atoi(string(rc[:]))
if err != nil {
return shim.Error(err.Error())
}
logger.Infof("Status = %d \n", vrc)
return pb.Response{
Status: int32(vrc),
Payload: transientData,
}
}
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) + "\"}"
logger.Infof("Query Response:%s\n", jsonResp)
return shim.Success(Avalbytes)
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
logger.Errorf("Error starting Simple chaincode: %s", err)
}
}
java语言版
项目结构
项目依赖
1)build.gradle文件
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.3'
id 'java'
}
group 'org.hyperledger.fabric-chaincode-java'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile group: 'org.hyperledger.fabric-chaincode-java', name: 'fabric-chaincode-shim', version: '1.4.1'
}
shadowJar {
//这个名字必须是chaincode
baseName = 'chaincode'
version = null
classifier = null
manifest {
attributes 'Main-Class': 'org.hyperledger.fabric.example.SimpleChaincode'
}
}
2)settings.gradle文件
rootProject.name = 'fabric-chaincode-example-gradle'
项目代码
package org.hyperledger.fabric.example; import java.util.List; import com.google.protobuf.ByteString; import io.netty.handler.ssl.OpenSsl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.ChaincodeStub; import static java.nio.charset.StandardCharsets.UTF_8; public class SimpleChaincode extends ChaincodeBase { private static Log _logger = LogFactory.getLog(SimpleChaincode.class); @Override public Response init(ChaincodeStub stub) { try { _logger.info("Init java simple chaincode"); String func = stub.getFunction(); if (!func.equals("init")) { return newErrorResponse("function other than init is not supported"); } List<String> args = stub.getParameters(); if (args.size() != 4) { newErrorResponse("Incorrect number of arguments. Expecting 4"); } // Initialize the chaincode
String account1Key = args.get(0); int account1Value = Integer.parseInt(args.get(1)); String account2Key = args.get(2); int account2Value = Integer.parseInt(args.get(3)); _logger.info(String.format("account %s, value = %s; account %s, value %s", account1Key, account1Value, account2Key, account2Value)); stub.putStringState(account1Key, args.get(1)); stub.putStringState(account2Key, args.get(3)); return newSuccessResponse(); } catch (Throwable e) { return newErrorResponse(e); } } @Override public Response invoke(ChaincodeStub stub) { try { _logger.info("Invoke java simple chaincode"); String func = stub.getFunction(); List<String> params = stub.getParameters(); if (func.equals("move")) { return move(stub, params); } if (func.equals("delete")) { return delete(stub, params); } if (func.equals("query")) { return query(stub, params); } return newErrorResponse("Invalid invoke function name. Expecting one of: [\"invoke\", \"delete\", \"query\"]"); } catch (Throwable e) { return newErrorResponse(e); } } private Response move(ChaincodeStub stub, List<String> args) { if (args.size() != 3) { return newErrorResponse("Incorrect number of arguments. Expecting 3"); } String accountFromKey = args.get(0); String accountToKey = args.get(1); String accountFromValueStr = stub.getStringState(accountFromKey); if (accountFromValueStr == null) { return newErrorResponse(String.format("Entity %s not found", accountFromKey)); } int accountFromValue = Integer.parseInt(accountFromValueStr); String accountToValueStr = stub.getStringState(accountToKey); if (accountToValueStr == null) { return newErrorResponse(String.format("Entity %s not found", accountToKey)); } int accountToValue = Integer.parseInt(accountToValueStr); int amount = Integer.parseInt(args.get(2)); if (amount > accountFromValue) { return newErrorResponse(String.format("not enough money in account %s", accountFromKey)); } accountFromValue -= amount; accountToValue += amount; _logger.info(String.format("new value of A: %s", accountFromValue)); _logger.info(String.format("new value of B: %s", accountToValue)); stub.putStringState(accountFromKey, Integer.toString(accountFromValue)); stub.putStringState(accountToKey, Integer.toString(accountToValue)); _logger.info("Transfer complete"); return newSuccessResponse("invoke finished successfully", ByteString.copyFrom(accountFromKey + ": " + accountFromValue + " " + accountToKey + ": " + accountToValue, UTF_8).toByteArray()); } // Deletes an entity from state
private Response delete(ChaincodeStub stub, List<String> args) { if (args.size() != 1) { return newErrorResponse("Incorrect number of arguments. Expecting 1"); } String key = args.get(0); // Delete the key from the state in ledger
stub.delState(key); return newSuccessResponse(); } // query callback representing the query of a chaincode
private Response query(ChaincodeStub stub, List<String> args) { if (args.size() != 1) { return newErrorResponse("Incorrect number of arguments. Expecting name of the person to query"); } String key = args.get(0); //byte[] stateBytes
String val = stub.getStringState(key); if (val == null) { return newErrorResponse(String.format("Error: state for %s is null", key)); } _logger.info(String.format("Query Response:\nName: %s, Amount: %s\n", key, val)); return newSuccessResponse(val, ByteString.copyFrom(val, UTF_8).toByteArray()); } public static void main(String[] args) { System.out.println("OpenSSL avaliable: " + OpenSsl.isAvailable()); new SimpleChaincode().start(args); } }