state

package
v0.6.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 25, 2021 License: MIT Imports: 12 Imported by: 0

README

Working with Hyperledger Fabric chaincode state with CCKit

Chaincode is a domain specific program which relates to specific business process. It programmatically accesses two distinct pieces of the ledger – a blockchain, which immutably records the history of all transactions, and a world state that holds a cache of the current value of these states. The job of a smart contract developer is to take an existing business process that might govern financial prices or delivery conditions, and express it as a smart contract in a programming language

Smart contracts primarily put, get and delete states in the world state, and can also query the state change history. Chaincode “shim” APIs implements ChaincodeStubInterface which contain methods for access and modify the ledger, and to make invocations between chaincodes. Main methods are:

  • GetState(key string) ([]byte, error) performs a query to retrieve information about the current state of a business object

  • PutState(key string, value []byte) error creates a new business object or modifies an existing one in the ledger world state

  • DelState(key string) error removes of a business object from the current state of the ledger, but not its history

  • GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error) queries the state in the ledger based on given partial composite key

  • GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) returns a history of key values across time.

All this methods use string key as record identifier and slice of bytes as state value. Most of examples uses JSON documents as chaincode state value. Hyperledger Fabric supports both LevelDB as CouchDB to serve as state database, holding the latest state of each object. LevelDB is the default key-value state database embedded in every peer. CouchDB is an optional alternative external state database with more features - it supports rich queries against JSON documents in chaincode state, whereas LevelDB only supports queries against keys.

Querying and updating state with ChaincodeStubInterface methods

As shown in many examples, assets can be represented as complex structures - Golang structs. The chaincode itself can store data as a string in a key/value pair setup. Thus, we need to marshal struct to JSON string before putting into chaincode state and unmarshal after getting from state.

With ChaincodeStubInterface methods these operations looks like this:

    ct := ContractType{}
    
    err := json.Unmarshal([]byte(args[0]), &req)
    if err != nil {
        return shim.Error(err.Error())
    }
    
    key, err := stub.CreateCompositeKey(prefixContractType, []string{req.UUID})
    if err != nil {
        return shim.Error(err.Error())
    }
    
    valAsBytes, err := stub.GetState(key)
    if err != nil {
        return shim.Error(err.Error())
    }
    if len(valAsBytes) == 0 {
        return shim.Error("Contract Type could not be found")
    }
    err = json.Unmarshal(valAsBytes, &ct)
    if err != nil {
        return shim.Error(err.Error())
    }
    
    ct.Active = req.Active
    
    valAsBytes, err = json.Marshal(ct)
    if err != nil {
        return shim.Error(err.Error())
    }
    
    err = stub.PutState(key, valAsBytes)
    if err != nil {
        return shim.Error(err.Error())
    }
    
    return shim.Success(nil)

In the example above smart contract code explicitly performs many auxiliary actions:

  • Creating composite key
  • Unmarshaling data after receiving it from state
  • Marshaling data before placing it to state

Modelling chaincode state with CCKit

State methods wrapper

CCKit contains wrapper on ChaincodeStubInterface methods to working with chaincode state. This methods simplifies chaincode key creation and data transformation during working with chaincode state.

type State interface {
    // Get returns value from state, converted to target type
    // entry can be Key (string or []string) or type implementing Keyer interface
    Get(entry interface{}, target ...interface{}) (result interface{}, err error)
    
    // Get returns value from state, converted to int
    // entry can be Key (string or []string) or type implementing Keyer interface
    GetInt(entry interface{}, defaultValue int) (result int, err error)
    
    // GetHistory returns slice of history records for entry, with values converted to target type
    // entry can be Key (string or []string) or type implementing Keyer interface
    GetHistory(entry interface{}, target interface{}) (result HistoryEntryList, err error)
    
    // Exists returns entry existence in state 
    // entry can be Key (string or []string) or type implementing Keyer interface
    Exists(entry interface{}) (exists bool, err error)
    
    // Put returns result of putting entry to state
    // entry can be Key (string or []string) or type implementing Keyer interface
    // if entry is implements Keyer interface and it's struct or type implementing
    // ToByter interface value can be omitted
    Put(entry interface{}, value ...interface{}) (err error)
    
    // Insert returns result of inserting entry to state
    // If same key exists in state error wil be returned
    // entry can be Key (string or []string) or type implementing Keyer interface
    // if entry is implements Keyer interface and it's struct or type implementing
    // ToByter interface value can be omitted
    Insert(entry interface{}, value ...interface{}) (err error)
    
    // List returns slice of target type
    // namespace can be part of key (string or []string) or entity with defined mapping
    List(namespace interface{}, target ...interface{}) (result []interface{}, err error)
    
    // Delete returns result of deleting entry from state
    // entry can be Key (string or []string) or type implementing Keyer interface
    Delete(entry interface{}) (err error)

	...
}
Converting from/to bytes while operating with chaincode state

State wrapper allows to automatically marshal golang type to/from slice of bytes. This type can be:

type (
	// FromByter interface supports FromBytes func for converting from slice of bytes to target type
	FromByter interface {
		FromBytes([]byte) (interface{}, error)
	}

	// ToByter interface supports ToBytes func for converting to slice of bytes from source type
	ToByter interface {
		ToBytes() ([]byte, error)
	}
)
  • Golang struct or one of supported types ( int, string, []string)
  • Protobuf golang struct

Golang structs automatically marshals/ unmarshals using json.Marshal and and json.Umarshal methods. proto.Marshal and proto.Unmarshal is used to convert protobuf.

Creating state keys

In the chaincode data model we often need to store many instances of one type on the ledger, such as multiple commercial papers, letters of credit, and so on. In this case, the keys of those instances will be typically constructed from a combination of attributes— for example:

CommercialPaper + {Issuer} + {PaperId}

yielding series of chaincode state entries keys [ CommercialPaperIssuer1Id1, CommercialPaperIssuer2Id2, ...]

The logic of creation primary key of an instance can be customized in the code, or API functions can be provided in SHIM to construct a composite key (in other words, a unique key) of an instance based on a combination of several attributes. Composite keys can then be used as a normal string key to record and retrieve values using the PutState() and GetState() functions.

The following snippet shows a list of functions that create and work with composite keys:

// The function creates a key by combining the attributes into a single string.
// The arguments must be valid utf8 strings and must not contain U+0000 (nil byte) and U+10FFFF charactres.
func CreateCompositeKey(objectType string, attributes []string) (string, error)

// The function splits the compositeKey into attributes from which the key was formed.
// This function is useful for extracting attributes from keys returned by range queries.
func SplitCompositeKey(compositeKey string) (string, []string, error)

When putting or getting data to/from chaincode state you must provide key. CCKit have 3 options for dealing with entries key:

  • Key can be passed explicit:
c.State().Put ( `my-key`, &myStructInstance)
  • Key type can implement Keyer interface
    Key []string

    // Keyer interface for entity containing logic of its key creation
    Keyer interface {
        Key() (Key, error)
    }

Key type - is essentially slice of string, this slice will be automatically converted to string using shim.CreateCompositeKey method.

and in chaincode you need to provide only type instance

c.State().Put (&myStructInstance)
  • Type can have associate mapping

Mapping defines rules for namespace, primary and other key creation. Mapping mainly used with protobuf state schema.

Range queries

As well as retrieving assets with a unique key, SHIM offers API functions the opportunity to retrieve sets of assets based on a range criteria. Moreover, composite keys can be modeled to enable queries against multiple components of the key.

The range functions return an iterator (StateQueryIteratorInterface) over a set of keys matching the query criteria. The returned keys are in lexical order. Additionally, when a composite key has multiple attributes, the range query function, GetStateByPartialCompositeKey(), can be used to search for keys matching a subset of the attributes.

For example, the key of a CommercialPaper composed of Issuer and PaperId attributes can be searched for entries only from one Issuer.

Protobuf state example

This example uses Commercial paper scenario and implements same functionality as Node.JS chaincode sample from official documentation. Example code located here.

Protobuf schema advantages:

  1. Schema abstraction layer

Encoding the semantics of your business objects once, in proto format, is enough to help ensure that the signal doesn’t get lost between applications, and that the boundaries you create enforce your business rules.

  1. Extensions - validators etc

Protobuf v3 does not support validating required parameters, but there are third party projects for proto validation, for example https://github.com/mwitkow/go-proto-validators. It allows to encode, at the schema level, the shape of your data structure, and the validation rules.

  1. Easy Language Interoperability Because Protocol Buffers are implemented in a variety of languages, they make interoperability between polyglot applications in your architecture that much simpler. If you’re introducing a new service using Java or Node.Js SDK you simply have to hand the proto file to the code generator written in the target language and you have guarantees about the safety and interoperability between those architectures.
Defining model

Protobuf (short for Protocol buffers) is a way of encoding structured data in an efficient and extensible format. 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 (or ant) that implements automatic encoding and parsing of the protocol buffer data with an efficient binary format. The generated class provides getters and setters for the fields that make up a protocol buffer and takes care of the details of reading and writing the protocol buffer as a unit.

In Commercial Paper example first we define messages, that will be stored in chaincode state or as events:

  • CommercialPaper will be stored in chaincode state
  • CommercialPaperId defines unique id part of commercial paper message
  • IssueCommercialPaper payload for issue transaction and event triggered when new commercial paper issued
  • BuyCommercialPaper payload for buy transaction and event triggered when commercial paper change owner
  • RedeemCommercialPaper payload for redeem transaction and event triggered when commercial paper redeemed
syntax = "proto3";
package schema;

import "google/protobuf/timestamp.proto";

message CommercialPaper {

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

    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;
}

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

// 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;
}

// 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;
}
Defining protobuf to chaincode state mapping

Protocol buffers to chaincode mapper can be used to store schema instances in chaincode state. Every schema type (protobuf or struct) can have mapping rules:

  • Primary key creation logic
  • Namespace logic
  • Secondary key creation logic

For example, in definition below, we defined thant schema.CommercialPaper mapped to chaincode state with key attributes from schema.CommercialPaperId message (Issuer, PaperNumber). Also we define event

var (
    // State mappings
   	StateMappings = m.StateMappings{}.
   		//key namespace will be <`CommercialPaper`, Issuer, PaperNumber>
   		Add(&schema.CommercialPaper{}, m.PKeySchema(&schema.CommercialPaperId{}))
   
   	// EventMappings
   	EventMappings = m.EventMappings{}.
   		// event name will be `IssueCommercialPaper`,  payload - same as issue payload
   		Add(&schema.IssueCommercialPaper{}).
   		Add(&schema.BuyCommercialPaper{}).
   		Add(&schema.RedeemCommercialPaper{})
)
Chaincode
package cpaper

import (
	"fmt"
	
	"github.com/pkg/errors"
	"github.com/optherium/cckit/examples/cpaper/schema"
	"github.com/optherium/cckit/extensions/debug"
	"github.com/optherium/cckit/extensions/encryption"
	"github.com/optherium/cckit/extensions/owner"
	"github.com/optherium/cckit/router"
	"github.com/optherium/cckit/router/param/defparam"
	m "github.com/optherium/cckit/state/mapping"
)


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).

		// Get method has 2 params - commercial paper primary key components
		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)
}


func cpaperList(c router.Context) (interface{}, error) {
	// commercial paper key is composite key <`CommercialPaper`>, {Issuer}, {PaperNumber} >
	// where `CommercialPaper` - namespace of this type
	// list method retrieves entries from chaincode state 
	// using GetStateByPartialCompositeKey method, then unmarshal received from state bytes via proto.Ummarshal method
	// and creates slice of *schema.CommercialPaper
	return c.State().List(&schema.CommercialPaper{})
}

func cpaperIssue(c router.Context) (interface{}, error) {
	var (
		issue  = c.Param().(*schema.IssueCommercialPaper) //default parameter
		cpaper = &schema.CommercialPaper{
			Issuer:       issue.Issuer,
			PaperNumber:  issue.PaperNumber,
			Owner:        issue.Issuer,
			IssueDate:    issue.IssueDate,
			MaturityDate: issue.MaturityDate,
			FaceValue:    issue.FaceValue,
			State:        schema.CommercialPaper_ISSUED, // initial state
		}
		err error
	)

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

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

func cpaperBuy(c router.Context) (interface{}, error) {

	var (
		cpaper *schema.CommercialPaper

		// but tx payload
		buy = c.Param().(*schema.BuyCommercialPaper)

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

	if err != nil {
		return nil, errors.Wrap(err, `not found`)
	}
	cpaper = cp.(*schema.CommercialPaper)

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

	// First buy 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 = buy.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(buy); err != nil {
		return nil, err
	}
	return cpaper, c.State().Put(cpaper)
}

func cpaperRedeem(c router.Context) (interface{}, error) {
	
	// implement me

	return nil, nil
}

func cpaperGet(c router.Context) (interface{}, error) {
	return c.State().Get(c.Param().(*schema.CommercialPaperId))
}

func cpaperDelete(c router.Context) (interface{}, error) {
	return nil, c.State().Delete(c.Param().(*schema.CommercialPaperId))
}
Tests

We can test all chaincode use case scenarios using MockStub

package mapping_test

import (
	"testing"

	"github.com/hyperledger/fabric/protos/peer"

	"github.com/golang/protobuf/ptypes"

	"github.com/golang/protobuf/proto"
	"github.com/optherium/cckit/examples/cpaper/schema"
	"github.com/optherium/cckit/examples/cpaper/testdata"
	"github.com/optherium/cckit/state"

	"github.com/optherium/cckit/examples/cpaper"

	examplecert "github.com/optherium/cckit/examples/cert"
	"github.com/optherium/cckit/identity"
	testcc "github.com/optherium/cckit/testing"
	expectcc "github.com/optherium/cckit/testing/expect"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func TestState(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "State suite")
}

var (
	actors   identity.Actors
	cPaperCC *testcc.MockStub
	err      error
)
var _ = Describe(`Mapping`, func() {

	BeforeSuite(func() {
		actors, err = identity.ActorsFromPemFile(`SOME_MSP`, map[string]string{
			`owner`: `s7techlab.pem`,
		}, examplecert.Content)

		Expect(err).To(BeNil())

		//Create commercial papers chaincode mock - protobuf based schema
		cPaperCC = testcc.NewMockStub(`cpapers`, cpaper.NewCC())
		cPaperCC.From(actors[`owner`]).Init()

	})

	Describe(`Protobuf based schema`, func() {
		It("Allow to add data to chaincode state", func(done Done) {

			events := cPaperCC.EventSubscription()
			expectcc.ResponseOk(cPaperCC.Invoke(`issue`, &testdata.CPapers[0]))

			Expect(<-events).To(BeEquivalentTo(&peer.ChaincodeEvent{
				EventName: `IssueCommercialPaper`,
				Payload:   testcc.MustProtoMarshal(&testdata.CPapers[0]),
			}))

			expectcc.ResponseOk(cPaperCC.Invoke(`issue`, &testdata.CPapers[1]))
			expectcc.ResponseOk(cPaperCC.Invoke(`issue`, &testdata.CPapers[2]))

			close(done)
		}, 0.2)

		It("Disallow to insert entries with same keys", func() {
			expectcc.ResponseError(cPaperCC.Invoke(`issue`, &testdata.CPapers[0]))
		})

		It("Allow to get entry list", func() {
			cpapers := expectcc.PayloadIs(cPaperCC.Query(`list`), &[]schema.CommercialPaper{}).([]schema.CommercialPaper)
			Expect(len(cpapers)).To(Equal(3))
			Expect(cpapers[0].Issuer).To(Equal(testdata.CPapers[0].Issuer))
			Expect(cpapers[0].PaperNumber).To(Equal(testdata.CPapers[0].PaperNumber))
		})

		It("Allow to get entry raw protobuf", func() {
			cp := testdata.CPapers[0]
			cpaperProtoFromCC := cPaperCC.Query(`get`, &schema.CommercialPaperId{Issuer: cp.Issuer, PaperNumber: cp.PaperNumber}).Payload

			stateCpaper := &schema.CommercialPaper{
				Issuer:       cp.Issuer,
				PaperNumber:  cp.PaperNumber,
				Owner:        cp.Issuer,
				IssueDate:    cp.IssueDate,
				MaturityDate: cp.MaturityDate,
				FaceValue:    cp.FaceValue,
				State:        schema.CommercialPaper_ISSUED, // initial state
			}
			cPaperProto, _ := proto.Marshal(stateCpaper)
			Expect(cpaperProtoFromCC).To(Equal(cPaperProto))
		})

		It("Allow update data in chaincode state", func() {
			cp := testdata.CPapers[0]
			expectcc.ResponseOk(cPaperCC.Invoke(`buy`, &schema.BuyCommercialPaper{
				Issuer:       cp.Issuer,
				PaperNumber:  cp.PaperNumber,
				CurrentOwner: cp.Issuer,
				NewOwner:     `some-new-owner`,
				Price:        cp.FaceValue - 10,
				PurchaseDate: ptypes.TimestampNow(),
			}))

			cpaperFromCC := expectcc.PayloadIs(
				cPaperCC.Query(`get`, &schema.CommercialPaperId{Issuer: cp.Issuer, PaperNumber: cp.PaperNumber}),
				&schema.CommercialPaper{}).(*schema.CommercialPaper)

			// state is updated
			Expect(cpaperFromCC.State).To(Equal(schema.CommercialPaper_TRADING))
			Expect(cpaperFromCC.Owner).To(Equal(`some-new-owner`))
		})

		It("Allow to delete entry", func() {

			cp := testdata.CPapers[0]
			toDelete := &schema.CommercialPaperId{Issuer: cp.Issuer, PaperNumber: cp.PaperNumber}

			expectcc.ResponseOk(cPaperCC.Invoke(`delete`, toDelete))
			cpapers := expectcc.PayloadIs(cPaperCC.Invoke(`list`), &[]schema.CommercialPaper{}).([]schema.CommercialPaper)

			Expect(len(cpapers)).To(Equal(2))
			expectcc.ResponseError(cPaperCC.Invoke(`get`, toDelete), state.ErrKeyNotFound)
		})
	})

})

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrEmptyChaincodeResponsePayload = errors.New(`empty chaincode response payload`)
)

Functions

func ConvertFromBytes added in v0.3.0

func ConvertFromBytes(bb []byte, config ...interface{}) (interface{}, error)

func ConvertToBytes added in v0.3.0

func ConvertToBytes(v interface{}, config ...interface{}) ([]byte, error)

func InvokeChaincode

func InvokeChaincode(
	stub shim.ChaincodeStubInterface, chaincodeName string, args []interface{},
	channel string, target interface{}) (interface{}, error)

InvokeChaincode locally calls the specified chaincode and converts result into target data type

func KeyAsIs added in v0.4.1

func KeyAsIs(key []string) ([]string, error)

ConvertKey returns string parts of composite key

func KeyError

func KeyError(key *TransformedKey) error

KeyError error with key

func KeyToString added in v0.6.3

func KeyToString(stub shim.ChaincodeStubInterface, key Key) (string, error)

func NameAsIs added in v0.4.1

func NameAsIs(name string) (string, error)

func NormalizeEventName added in v0.4.1

func NormalizeEventName(name interface{}) (string, error)

func StringsIdFromStr added in v0.4.3

func StringsIdFromStr(idString string) []string

StringsIdFromStr helper for restoring []string key

func StringsIdToStr added in v0.4.3

func StringsIdToStr(idSlice []string) string

StringsIdToStr helper for passing []string key

Types

type Combination added in v0.3.11

type Combination struct {
	Type         CombinationType
	Value        []interface{}
	Filters      []Filter
	Conditions   []interface{}
	Combinations []*Combination
	Builder      *QueryBuilder
}

Combination used for and,or,nor,all operators

func (*Combination) AddCombination added in v0.3.11

func (c *Combination) AddCombination(combinationType CombinationType, filters ...interface{}) *Combination

AddCombination adds a combination to an existing one for nesting

type CombinationType added in v0.3.11

type CombinationType string

CombinationType type of combination

const (
	// Matches if all the selectors in the array match.
	AND CombinationType = "$and"
	// Matches if any of the selectors in the array match. All selectors must use the same index.
	OR CombinationType = "$or"
	// Matches an array value if it contains all the elements of the argument array.
	ALL CombinationType = "$all"
	// Matches if none of the selectors in the array match.
	NOR CombinationType = "$nor"
)

type EqualCondition added in v0.3.12

type EqualCondition struct {
	Value interface{} `json:"$eq"`
}

EqualCondition The field is equal to the argument

type Event added in v0.4.1

type Event interface {
	Set(entry interface{}, value ...interface{}) error
	UseSetTransformer(ToBytesTransformer) Event
	UseNameTransformer(StringTransformer) Event
}

Event interface for working with events in chaincode

type EventImpl added in v0.4.1

type EventImpl struct {
	NameTransformer StringTransformer
	SetTransformer  ToBytesTransformer
	// contains filtered or unexported fields
}

func NewEvent added in v0.4.1

func NewEvent(stub shim.ChaincodeStubInterface) *EventImpl

NewEvent creates wrapper on shim.ChaincodeStubInterface for working with events

func (*EventImpl) ArgNameValue added in v0.4.1

func (e *EventImpl) ArgNameValue(arg interface{}, values []interface{}) (name string, value interface{}, err error)

func (*EventImpl) Set added in v0.4.1

func (e *EventImpl) Set(entry interface{}, values ...interface{}) error

func (*EventImpl) UseNameTransformer added in v0.4.1

func (e *EventImpl) UseNameTransformer(nt StringTransformer) Event

func (*EventImpl) UseSetTransformer added in v0.4.1

func (e *EventImpl) UseSetTransformer(tb ToBytesTransformer) Event

type ExistCondition added in v0.3.12

type ExistCondition struct {
	Value bool `json:"$exists"`
}

ExistCondition Check whether the field exists or not, regardless of its value.

type Filter added in v0.3.11

type Filter struct {
	Field string
	Value interface{}
}

Filter used to filter on a single field

type FromBytesTransformer added in v0.3.0

type FromBytesTransformer func(bb []byte, config ...interface{}) (interface{}, error)

ToBytesTransformer is used after getState operation for convert value

type GreaterThanCondition added in v0.3.12

type GreaterThanCondition struct {
	Value interface{} `json:"$gt"`
}

GreaterThanCondition The field is greater than to the argument.

type GreaterThanOrEqualCondition added in v0.3.12

type GreaterThanOrEqualCondition struct {
	Value interface{} `json:"$gte"`
}

GreaterThanCondition The field is greater than or equal to the argument.

type HistoryEntry

type HistoryEntry struct {
	TxId      string      `json:"txId"`
	Timestamp int64       `json:"timestamp"`
	IsDeleted bool        `json:"isDeleted"`
	Value     interface{} `json:"value"`
}

HistoryEntry struct containing history information of a single entry

type HistoryEntryList

type HistoryEntryList []HistoryEntry

HistoryEntryList list of history entries

type Impl added in v0.6.3

type Impl struct {
	StateKeyTransformer KeyTransformer
	StateGetTransformer FromBytesTransformer
	StatePutTransformer ToBytesTransformer
	// contains filtered or unexported fields
}

func NewState added in v0.4.1

func NewState(stub shim.ChaincodeStubInterface, logger *shim.ChaincodeLogger) *Impl

NewState creates wrapper on shim.ChaincodeStubInterface for working with state

func (*Impl) Delete added in v0.6.3

func (s *Impl) Delete(entry interface{}) error

Delete entry from state

func (*Impl) DeletePrivate added in v0.6.3

func (s *Impl) DeletePrivate(collection string, entry interface{}) error

Delete entry from private state

func (*Impl) Exists added in v0.6.3

func (s *Impl) Exists(entry interface{}) (bool, error)

Exists check entry with key exists in chaincode state

func (*Impl) ExistsPrivate added in v0.6.3

func (s *Impl) ExistsPrivate(collection string, entry interface{}) (bool, error)

PrivateExists check entry with key exists in chaincode private state

func (*Impl) Get added in v0.6.3

func (s *Impl) Get(entry interface{}, config ...interface{}) (interface{}, error)

Get data by key from state, trying to convert to target interface

func (*Impl) GetHistory added in v0.6.3

func (s *Impl) GetHistory(entry interface{}, target interface{}) (HistoryEntryList, error)

GetHistory by key from state, trying to convert to target interface

func (*Impl) GetInt added in v0.6.3

func (s *Impl) GetInt(key interface{}, defaultValue int) (int, error)

func (*Impl) GetPrivate added in v0.6.3

func (s *Impl) GetPrivate(collection string, entry interface{}, config ...interface{}) (interface{}, error)

Get data by key from private state, trying to convert to target interface

func (*Impl) Insert added in v0.6.3

func (s *Impl) Insert(entry interface{}, values ...interface{}) error

Insert value into chaincode state, returns error if key already exists

func (*Impl) InsertPrivate added in v0.6.3

func (s *Impl) InsertPrivate(collection string, entry interface{}, values ...interface{}) (err error)

Insert value into chaincode private state, returns error if key already exists

func (*Impl) Key added in v0.6.3

func (s *Impl) Key(key interface{}) (*TransformedKey, error)

func (*Impl) List added in v0.6.3

func (s *Impl) List(namespace interface{}, target ...interface{}) (interface{}, error)

List data from state using objectType prefix in composite key, trying to convert to target interface. Keys - additional components of composite key

func (*Impl) ListPrivate added in v0.6.3

func (s *Impl) ListPrivate(collection string, usePrivateDataIterator bool, namespace interface{}, target ...interface{}) (interface{}, error)

List data from private state using objectType prefix in composite key, trying to convert to target interface. Keys - additional components of composite key If usePrivateDataIterator is true, used private state for iterate over objects if false, used public state for iterate over keys and GetPrivateData for each key

func (*Impl) Logger added in v0.6.3

func (s *Impl) Logger() *shim.ChaincodeLogger

func (*Impl) PaginateList added in v0.6.3

func (s *Impl) PaginateList(objectType interface{}, target interface{}, limit int32, start string) (result []interface{}, end string, err error)

func (*Impl) Put added in v0.6.3

func (s *Impl) Put(entry interface{}, values ...interface{}) error

Put data value in state with key, trying convert data to []byte

func (*Impl) PutPrivate added in v0.6.3

func (s *Impl) PutPrivate(collection string, entry interface{}, values ...interface{}) (err error)

Put data value in private state with key, trying convert data to []byte

func (*Impl) RichListQuery added in v0.6.3

func (s *Impl) RichListQuery(query string, target interface{}, pageSize int32, bookmark string) (result []interface{}, newBookmark string, err error)

func (*Impl) RichQuery added in v0.6.3

func (s *Impl) RichQuery(query string, target interface{}, pageSize int) ([]interface{}, int, error)

func (*Impl) UseKeyTransformer added in v0.6.3

func (s *Impl) UseKeyTransformer(kt KeyTransformer) State

func (*Impl) UseStateGetTransformer added in v0.6.3

func (s *Impl) UseStateGetTransformer(fb FromBytesTransformer) State

func (*Impl) UseStatePutTransformer added in v0.6.3

func (s *Impl) UseStatePutTransformer(tb ToBytesTransformer) State

type InCondition added in v0.3.12

type InCondition struct {
	Value []interface{} `json:"$in"`
}

InCondition The document field must exist in the list provided.

type Key

type Key []string

func NormalizeStateKey added in v0.4.1

func NormalizeStateKey(key interface{}) (Key, error)

func (Key) Append added in v0.4.1

func (k Key) Append(key Key) Key

type KeyFunc added in v0.4.1

type KeyFunc func() (Key, error)

KeyerFunc func(string) ([]string, error)

type KeyTransformer added in v0.4.1

type KeyTransformer func(key []string) ([]string, error)

KeyTransformer is used before putState operation for convert key

type KeyValue added in v0.4.1

type KeyValue interface {
	Keyer
	convert.ToByter
}

KeyValue interface combines Keyer as ToByter methods - state entry representation

type Keyer

type Keyer interface {
	Key() (Key, error)
}

Keyer interface for entity containing logic of its key creation

func StringKeyer

func StringKeyer(str string, keyer KeyerFunc) Keyer

StringKeyer constructor for struct implementing Keyer interface

type KeyerFunc

type KeyerFunc func(string) (Key, error)

KeyerFunc transforms string to key

type LessThanCondition added in v0.3.12

type LessThanCondition struct {
	Value interface{} `json:"$lt"`
}

LessThanCondition The field is less than the argument

type LessThanOrEqualCondition added in v0.3.12

type LessThanOrEqualCondition struct {
	Value interface{} `json:"$lte"`
}

LessThanOrEqualCondition The field is less than or equal to the argument.

type ModCondition added in v0.3.12

type ModCondition struct {
	Value [2]int `json:"$mod"`
}

ModCondition [Divisor, Remainder] Divisor and Remainder are both positive or negative integers. Non-integer values result in a 404. Matches documents where field % Divisor == Remainder is true, and only when the document field is an integer.

type NameValue added in v0.4.1

type NameValue interface {
	Namer
	convert.ToByter
}

NameValue interface combines Name() as ToByter methods - event representation

type Namer added in v0.4.1

type Namer interface {
	Name() (string, error)
}

type NotEqualCondition added in v0.3.12

type NotEqualCondition struct {
	Value interface{} `json:"$neq"`
}

NotEqualCondition The field is not equal to the argument

type QueryBuilder added in v0.3.11

type QueryBuilder struct {
	// contains filtered or unexported fields
}

QueryBuilder used to generate couchDB queries

func NewQB added in v0.3.11

func NewQB() *QueryBuilder

NewQB create a new instance of the QueryBuilder

func (*QueryBuilder) AddCombination added in v0.3.11

func (builder *QueryBuilder) AddCombination(combinationType CombinationType, filters ...interface{}) *Combination

AddCombination adds a combination to the builder query

func (*QueryBuilder) AddCondition added in v0.3.11

func (builder *QueryBuilder) AddCondition(field string, condition interface{}) *QueryBuilder

AddCondition adds a pre-defined CouchDB condition filter to the CouchDB query

func (*QueryBuilder) AddField added in v0.3.11

func (builder *QueryBuilder) AddField(fields ...string) *QueryBuilder

AddField adds a field to the couchDB query

func (*QueryBuilder) AddFilter added in v0.3.11

func (builder *QueryBuilder) AddFilter(field string, value interface{}) *QueryBuilder

AddFilter adds a filter to filter on in the couchDB query

func (*QueryBuilder) AddManySorts added in v0.3.11

func (builder *QueryBuilder) AddManySorts(sortRequest []string) error

func (*QueryBuilder) AddSort added in v0.3.11

func (builder *QueryBuilder) AddSort(field string, sortOrder string) *QueryBuilder

AddSort adds a field to sort on in the couchDB query

func (*QueryBuilder) AddUseIndex added in v0.3.11

func (builder *QueryBuilder) AddUseIndex(index string) *QueryBuilder

func (*QueryBuilder) Build added in v0.3.11

func (builder *QueryBuilder) Build() (string, error)

Build constructs the query and outputs the final result

func (*QueryBuilder) SetDocType added in v0.3.11

func (builder *QueryBuilder) SetDocType(docType string) *QueryBuilder

SetDocType set the main doc type for the couchDB query

func (*QueryBuilder) SetLimit added in v0.3.11

func (builder *QueryBuilder) SetLimit(limit int) *QueryBuilder

SetLimit sets the limit for paging

func (*QueryBuilder) SetSkip added in v0.3.11

func (builder *QueryBuilder) SetSkip(skip int) *QueryBuilder

SetSkip sets the skip value for paging

type RegExCondition added in v0.3.12

type RegExCondition struct {
	Value string `json:"$regex"`
}

RegExCondition A regular expression pattern to match against the document field. Only matches when the field is a string value and matches the supplied regular expression. The matching algorithms are based on the Perl Compatible Regular Expression (PCRE) library. For more information about what is implemented, see the see the Erlang Regular Expressio

type SizeCondition added in v0.3.12

type SizeCondition struct {
	Value uint `json:"$size"`
}

SizeCondition Special condition to match the length of an array field in a document. Non-array fields cannot match this condition.

type State added in v0.3.0

type State interface {
	// Get returns value from state, converted to target type
	// entry can be Key (string or []string) or type implementing Keyer interface
	Get(entry interface{}, target ...interface{}) (result interface{}, err error)

	// Get returns value from state, converted to int
	// entry can be Key (string or []string) or type implementing Keyer interface
	GetInt(entry interface{}, defaultValue int) (result int, err error)

	// GetHistory returns slice of history records for entry, with values converted to target type
	// entry can be Key (string or []string) or type implementing Keyer interface
	GetHistory(entry interface{}, target interface{}) (result HistoryEntryList, err error)

	// Exists returns entry existence in state
	// entry can be Key (string or []string) or type implementing Keyer interface
	Exists(entry interface{}) (exists bool, err error)

	// Put returns result of putting entry to state
	// entry can be Key (string or []string) or type implementing Keyer interface
	// if entry is implements Keyer interface and it's struct or type implementing
	// ToByter interface value can be omitted
	Put(entry interface{}, value ...interface{}) (err error)

	// Insert returns result of inserting entry to state
	// If same key exists in state error wil be returned
	// entry can be Key (string or []string) or type implementing Keyer interface
	// if entry is implements Keyer interface and it's struct or type implementing
	// ToByter interface value can be omitted
	Insert(entry interface{}, value ...interface{}) (err error)

	// List returns slice of target type
	// namespace can be part of key (string or []string) or entity with defined mapping
	List(namespace interface{}, target ...interface{}) (result interface{}, err error)

	// Delete returns result of deleting entry from state
	// entry can be Key (string or []string) or type implementing Keyer interface
	Delete(entry interface{}) (err error)

	Logger() *shim.ChaincodeLogger

	UseKeyTransformer(KeyTransformer) State
	UseStateGetTransformer(FromBytesTransformer) State
	UseStatePutTransformer(ToBytesTransformer) State

	// GetPrivate returns value from private state, converted to target type
	// entry can be Key (string or []string) or type implementing Keyer interface
	GetPrivate(collection string, entry interface{}, target ...interface{}) (result interface{}, err error)

	// PutPrivate returns result of putting entry to private state
	// entry can be Key (string or []string) or type implementing Keyer interface
	// if entry is implements Keyer interface and it's struct or type implementing
	// ToByter interface value can be omitted
	PutPrivate(collection string, entry interface{}, value ...interface{}) (err error)

	// InsertPrivate returns result of inserting entry to private state
	// If same key exists in state error wil be returned
	// entry can be Key (string or []string) or type implementing Keyer interface
	// if entry is implements Keyer interface and it's struct or type implementing
	// ToByter interface value can be omitted
	InsertPrivate(collection string, entry interface{}, value ...interface{}) (err error)

	// ListPrivate returns slice of target type from private state
	// namespace can be part of key (string or []string) or entity with defined mapping
	// If usePrivateDataIterator is true, used private state for iterate over objects
	// if false, used public state for iterate over keys and GetPrivateData for each key
	ListPrivate(collection string, usePrivateDataIterator bool, namespace interface{}, target ...interface{}) (result interface{}, err error)

	// DeletePrivate returns result of deleting entry from private state
	// entry can be Key (string or []string) or type implementing Keyer interface
	DeletePrivate(collection string, entry interface{}) (err error)

	// ExistsPrivate returns entry existence in private state
	// entry can be Key (string or []string) or type implementing Keyer interface
	ExistsPrivate(collection string, entry interface{}) (exists bool, err error)

	// PaginateList allows to list keys by prefix with pagination
	PaginateList(objectType interface{}, target interface{}, pageSize int32, start string) (result []interface{}, end string, err error)

	// RichListQuery allows to perform rich state DB query using state DB syntax with bookmark pagination
	RichListQuery(query string, target interface{}, pageSize int32, bookmark string) (result []interface{}, newBookmark string, err error)

	// RichQuery allows to perform rich state DB query using state DB syntax
	RichQuery(query string, target interface{}, pageSize int) ([]interface{}, int, error)
}

State interface for chain code CRUD operations

type StateList added in v0.4.2

type StateList struct {
	// contains filtered or unexported fields
}

func NewStateList added in v0.4.2

func NewStateList(config ...interface{}) *StateList

func (*StateList) AddElementToList added in v0.6.3

func (sl *StateList) AddElementToList(elem interface{})

func (*StateList) Fill added in v0.4.2

func (sl *StateList) Fill(
	iter shim.StateQueryIteratorInterface, fromBytes FromBytesTransformer) (list interface{}, err error)

func (*StateList) Get added in v0.4.2

func (sl *StateList) Get() (list interface{}, err error)

type StringTransformer added in v0.4.1

type StringTransformer func(name string) (string, error)

NameTransformer is used before setEvent operation for convert name

type StringsKeyer added in v0.4.1

type StringsKeyer interface {
	Key() ([]string, error)
}

StringsKeys interface for entity containing logic of its key creation - backward compatibility

type ToBytesTransformer added in v0.3.0

type ToBytesTransformer func(v interface{}, config ...interface{}) ([]byte, error)

ToBytesTransformer is used before putState operation for convert payload

type TransformedKey added in v0.6.3

type TransformedKey struct {
	Origin Key
	Parts  Key
	String string
}

StateKey stores origin and transformed state key

type TypeCondition added in v0.3.12

type TypeCondition struct {
	Value string `json:"$type"`
}

TypeCondition Check the document field’s type. Valid values are "null", "boolean", "number", "string", "array", and "object".

Directories

Path Synopsis
Package schema is a generated protocol buffer package.
Package schema is a generated protocol buffer package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL