All Projects → s7techlab → Cckit

s7techlab / Cckit

Licence: mit
Programming toolkit for building Hyperledger Fabric Golang on-chain (chaincode) and off-chain applications

Programming Languages

go
31211 projects - #10 most used programming language
golang
3204 projects

Projects that are alternatives of or similar to Cckit

Eosfactory
Python-based EOS smart-contract development & testing framework
Stars: ✭ 247 (+47.9%)
Mutual labels:  smart-contracts, unit-testing
Convector
Smart Contract Systems the easy way. Open source development framework.
Stars: ✭ 147 (-11.98%)
Mutual labels:  smart-contracts, hyperledger-fabric
Blockchain Real Estate
🚀基于区块链的房地产交易系统小模型。提供销售和捐赠功能。本项目使用Hyperledger Fabric构建区块链网络, go编写智能合约,应用层使用gin+fabric-sdk-go调用合约。前端展示使用vue+element。体验地址:http://blockchain.togettoyou.com/web
Stars: ✭ 81 (-51.5%)
Mutual labels:  smart-contracts, hyperledger-fabric
Fabric Chaincode Java
Hyperledger Fabric Contract and Chaincode implementation for Java https://wiki.hyperledger.org/display/fabric
Stars: ✭ 188 (+12.57%)
Mutual labels:  smart-contracts, hyperledger-fabric
Meadow
Integrated Ethereum implementation and tool suite focused on Solidity testing and development.
Stars: ✭ 126 (-24.55%)
Mutual labels:  unit-testing, smart-contracts
mastering-blockchain
blockchain basis,logic,usage
Stars: ✭ 29 (-82.63%)
Mutual labels:  smart-contracts, hyperledger-fabric
Blockchainbooks.github.io
Blockchain Books
Stars: ✭ 139 (-16.77%)
Mutual labels:  smart-contracts, hyperledger-fabric
Ebtc
eBitcoin (eBTC) is an ERC20 token. Its primary utility is to provide an easy & fast payment solution. Its edge over other tokens is that it is capable of sending up to 255 payments in a single transaction.
Stars: ✭ 149 (-10.78%)
Mutual labels:  smart-contracts
Abot
Cross Platform C# web crawler framework built for speed and flexibility. Please star this project! +1.
Stars: ✭ 1,961 (+1074.25%)
Mutual labels:  unit-testing
Bookstore Ios
 Sample iOS App - A collection of examples and patterns for Unit Testing, UI Testing, handling Result/Optionals, writing documentation, and more. Details in README.
Stars: ✭ 147 (-11.98%)
Mutual labels:  unit-testing
Supersafebank
Sample Event Sourcing implementation with .NET Core
Stars: ✭ 142 (-14.97%)
Mutual labels:  unit-testing
Pivt
Helm charts for running and operating Hyperledger Fabric in Kubernetes. Previously hosted at https://github.com/APGGroeiFabriek/PIVT.
Stars: ✭ 159 (-4.79%)
Mutual labels:  hyperledger-fabric
Vstest Docs
Documentation for the Visual Studio Test Platform.
Stars: ✭ 165 (-1.2%)
Mutual labels:  unit-testing
Eos Java Rpc Wrapper
Stars: ✭ 162 (-2.99%)
Mutual labels:  smart-contracts
Pat
Prometheus Alert Testing utility
Stars: ✭ 156 (-6.59%)
Mutual labels:  unit-testing
Sablier
The protocol for real-time finance on the Ethereum blockchain
Stars: ✭ 147 (-11.98%)
Mutual labels:  smart-contracts
Eos Contract
Tutorial on writing smart contracts on EOS EOS智能合约教程,从零开始学习EOS智能合约, DApp安全漏洞(security)及攻击实践(EOS Contract)(Powered by Itleaks)
Stars: ✭ 163 (-2.4%)
Mutual labels:  smart-contracts
Testbook
🧪 📗 Unit test your Jupyter Notebooks the right way
Stars: ✭ 146 (-12.57%)
Mutual labels:  unit-testing
Systemwrapper
.NET library for easier testing of system APIs.
Stars: ✭ 153 (-8.38%)
Mutual labels:  unit-testing
Ico Contracts
Ethereum smart contracts that have been used during successful ICOs
Stars: ✭ 160 (-4.19%)
Mutual labels:  smart-contracts

Hyperledger Fabric chaincode kit (CCKit)

Go Report Card Build Coverage Status

Overview

A smart contract is code, invoked by a client application external to the blockchain network – that manages access and modifications to a set of key-value pairs in the World State. In Hyperledger Fabric, smart contracts are referred to as chaincode.

CCKit is a programming toolkit for developing and testing Hyperledger Fabric golang chaincodes. It enhances the development experience while providing developers components for creating more readable and secure smart contracts.

Chaincode examples

There are several chaincode "official" examples available:

and others

Main problems with existing examples are:

  • Working with chaincode state at very low level
  • Lots of code duplication (JSON marshalling / unmarshalling, validation, access control, etc)
  • Chaincode methods routing appeared only in HLF 1.4 and only in Node.Js chaincode
  • Uncompleted testing tools (MockStub)

CCKit features

Publications with usage examples

Examples based on CCKit

Installation

CCKit requires Go 1.11+ with modules support

Standalone

git clone [email protected]:s7techlab/cckit.git

go mod vendor

As dependency

go get -u github.com/s7techlab/cckit

Example - Commercial Paper chaincode

Scenario

Commercial paper scenario from official documentation describes a Hyperledger Fabric network, aimed to issue, buy and redeem commercial paper.

commercial paper network

5 steps to develop chaincode

Chaincode is a domain specific program which relates to specific business process. The job of a smart contract developer is to take an existing business process and express it as a smart contract in a programming language. Steps of chaincode development:

  1. Define chaincode model - schema for state entries, transaction payload and events
  2. Define chaincode interface
  3. Implement chaincode instantiate method
  4. Implement chaincode methods with business logic
  5. Create tests

Define chaincode model

With protocol buffers, you write a .proto description of the data structure you wish to store. From that, the protocol buffer compiler creates a golang struct that implements automatic encoding and parsing of the protocol buffer data with an efficient binary format (or json).

Code generation can be simplified with a short Makefile:

.: generate

generate:
	@echo "schema"
	@protoc -I=./ --go_out=./ ./*.proto

Chaincode state

The following file shows how to define the world state schema using protobuf.

examples/cpaper_extended/schema/state.proto

syntax = "proto3";

package cckit.examples.cpaper_extended.schema;
option go_package = "schema";

import "google/protobuf/timestamp.proto";

// Commercial Paper state entry
message CommercialPaper {

    enum State {
        ISSUED = 0;
        TRADING = 1;
        REDEEMED = 2;
    }

    // Issuer and Paper number comprises composite primary key of Commercial paper entry
    string issuer = 1;
    string paper_number = 2;

    string owner = 3;
    google.protobuf.Timestamp issue_date = 4;
    google.protobuf.Timestamp maturity_date = 5;
    int32 face_value = 6;
    State state = 7;

    // Additional unique field for entry
    string external_id = 8;
}

// CommercialPaperId identifier part
message CommercialPaperId {
    string issuer = 1;
    string paper_number = 2;
}

// Container for returning multiple entities
message CommercialPaperList {
    repeated CommercialPaper items = 1;
}

Chaincode transaction and events payload

This file defines the data payload used using in the business logic methods. In this example transaction and event payloads are exactly the same for the sake of brevity, but you could create a different schema for each type of payload.

examples/cpaper_extended/schema/payload.proto

// IssueCommercialPaper event
syntax = "proto3";

package cckit.examples.cpaper_extended.schema;
option go_package = "schema";

import "google/protobuf/timestamp.proto";
import "github.com/mwitkow/go-proto-validators/validator.proto";

// IssueCommercialPaper event
message IssueCommercialPaper {
    string issuer = 1;
    string paper_number = 2;
    google.protobuf.Timestamp issue_date = 3;
    google.protobuf.Timestamp maturity_date = 4;
    int32 face_value = 5;

    // external_id - another unique constraint
    string external_id = 6;
}

// BuyCommercialPaper event
message BuyCommercialPaper {
    string issuer = 1;
    string paper_number = 2;
    string current_owner = 3;
    string new_owner = 4;
    int32 price = 5;
    google.protobuf.Timestamp purchase_date = 6;
}

// RedeemCommercialPaper event
message RedeemCommercialPaper {
    string issuer = 1;
    string paper_number = 2;
    string redeeming_owner = 3;
    google.protobuf.Timestamp redeem_date = 4;
}

Define chaincode interface

In examples/cpaper_extended/chaincode.go file we will define the mappings, chaincode initialization method and business logic in the transaction methods. For brevity we will only display snippets of the code here, please refer to the original file for full example.

Firstly we define mapping rules. These specify the struct used to hold a specific chaincode state, it's primary key, list mapping, unique keys, etc. Then we define the schemas used for emitting events.

var (
	// State mappings
	StateMappings = m.StateMappings{}.
		// Create mapping for Commercial Paper entity
		Add(&schema.CommercialPaper{},
			// Key namespace will be <"CommercialPaper", Issuer, PaperNumber>
			m.PKeySchema(&schema.CommercialPaperId{}),
			// Structure of result for List method
			m.List(&schema.CommercialPaperList{}),
			// External Id is unique
			m.UniqKey("ExternalId"),
		)

	// EventMappings
	EventMappings = m.EventMappings{}.
		// Event name will be "IssueCommercialPaper", payload - same as issue payload
		Add(&schema.IssueCommercialPaper{}).
		// Event name will be "BuyCommercialPaper"
		Add(&schema.BuyCommercialPaper{}).
		// Event name will be "RedeemCommercialPaper"
		Add(&schema.RedeemCommercialPaper{})
)

CCKit uses router to define rules about how to map chaincode invocation to a particular handler, as well as what kind of middleware needs to be used during a request, for example how to convert incoming argument from []byte to target type (string, struct, etc).

func NewCC() *router.Chaincode {

	r := router.New(`commercial_paper`)

	// Mappings for chaincode state
	r.Use(m.MapStates(StateMappings))

	// Mappings for chaincode events
	r.Use(m.MapEvents(EventMappings))

	// Store in chaincode state information about chaincode first instantiator
	r.Init(owner.InvokeSetFromCreator)

	// Method for debug chaincode state
	debug.AddHandlers(r, `debug`, owner.Only)

	r.
		// read methods
		Query(`list`, cpaperList).

		Query(`get`, cpaperGet, defparam.Proto(&schema.CommercialPaperId{})).

		// txn methods
		Invoke(`issue`, cpaperIssue, defparam.Proto(&schema.IssueCommercialPaper{})).
		Invoke(`buy`, cpaperBuy, defparam.Proto(&schema.BuyCommercialPaper{})).
		Invoke(`redeem`, cpaperRedeem, defparam.Proto(&schema.RedeemCommercialPaper{})).
		Invoke(`delete`, cpaperDelete, defparam.Proto(&schema.CommercialPaperId{}))

	return router.NewChaincode(r)
}

Implement chaincode init method

In many cases during chaincode instantiation we need to define permissions for chaincode functions - "who is allowed to do this thing", incredibly important in the world of smart contracts. The most common and basic form of access control is the concept of ownership: there's one account (combination of MSP and certificate identifiers) that is the owner and can do administrative tasks on contracts. This approach is perfectly reasonable for contracts that only have a single administrative user.

CCKit provides owner extension for implementing ownership and access control in Hyperledger Fabric chaincodes. In the previous snippet, as an init method, we used owner.InvokeSetFromCreator, storing information which stores the information about who is the owner into the world state upon chaincode instantiation.

Implement business rules as chaincode methods

Now we have to define the actual business logic which will modify the world state when a transaction occurs. In this example we will show only the buy method for brevity. Please refer to examples/cpaper_extended/chaincode.go for full implementation.

func invokeCPaperBuy(c router.Context) (interface{}, error) {
	var (
		cpaper *schema.CommercialPaper

		// Buy transaction payload
		buyData = c.Param().(*schema.BuyCommercialPaper)

		// Get the current commercial paper state
		cp, err = c.State().Get(
			&schema.CommercialPaperId{Issuer: buyData.Issuer, PaperNumber: buyData.PaperNumber},
			&schema.CommercialPaper{})
	)

	if err != nil {
		return nil, errors.Wrap(err, "not found")
	}

	cpaper = cp.(*schema.CommercialPaper)

	// Validate current owner
	if cpaper.Owner != buyData.CurrentOwner {
		return nil, fmt.Errorf(
			"paper %s %s is not owned by %s",
			cpaper.Issuer, cpaper.PaperNumber, buyData.CurrentOwner)
	}

	// First buyData moves state from ISSUED to TRADING
	if cpaper.State == schema.CommercialPaper_ISSUED {
		cpaper.State = schema.CommercialPaper_TRADING
	}

	// Check paper is not already REDEEMED
	if cpaper.State == schema.CommercialPaper_TRADING {
		cpaper.Owner = buyData.NewOwner
	} else {
		return nil, fmt.Errorf(
			"paper %s %s is not trading.current state = %s",
			cpaper.Issuer, cpaper.PaperNumber, cpaper.State)
	}

	if err = c.Event().Set(buyData); err != nil {
		return nil, err
	}

	return cpaper, c.State().Put(cpaper)
}

Test chaincode functionality

And finally we should write tests to ensure our business logic is behaving as it should. Again, for brevity, we omitted most of the code from examples/cpaper_extended/chaincode_test.go. CCKit support chaincode testing with Mockstub.

var _ = Describe(`CommercialPaper`, func() {
	paperChaincode := testcc.NewMockStub(`commercial_paper`, NewCC())

	BeforeSuite(func() {
		// Init chaincode with admin identity
		expectcc.ResponseOk(
			paperChaincode.
				From(testdata.GetTestIdentity(MspName, path.Join("testdata", "admin", "admin.pem"))).
				Init())
	})

	Describe("Commercial Paper lifecycle", func() {
		// ...

		It("Allow buyer to buy commercial paper", func() {
			buyTransactionData := &schema.BuyCommercialPaper{
				Issuer:       IssuerName,
				PaperNumber:  "0001",
				CurrentOwner: IssuerName,
				NewOwner:     BuyerName,
				Price:        95000,
				PurchaseDate: ptypes.TimestampNow(),
			}

			expectcc.ResponseOk(paperChaincode.Invoke(`buy`, buyTransactionData))

			queryResponse := paperChaincode.Query("get", &schema.CommercialPaperId{
				Issuer:      IssuerName,
				PaperNumber: "0001",
			})

			paper := expectcc.PayloadIs(queryResponse, &schema.CommercialPaper{}).(*schema.CommercialPaper)

			Expect(paper.Owner).To(Equal(BuyerName))
			Expect(paper.State).To(Equal(schema.CommercialPaper_TRADING))

			Expect(<-paperChaincode.ChaincodeEventsChannel).To(BeEquivalentTo(&peer.ChaincodeEvent{
				EventName: `BuyCommercialPaper`,
				Payload:   testcc.MustProtoMarshal(buyTransactionData),
			}))

			paperChaincode.ClearEvents()
		})

		// ...

	})
})
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].