github.com/s7techlab/cckit@v0.10.5/state/README.md (about)

     1  # Working with Hyperledger Fabric chaincode state with CCKit
     2  
     3  Chaincode is a domain specific program which relates to specific business process. It programmatically accesses 
     4  two distinct pieces of the ledger – a blockchain, which immutably records the history of all transactions, and a world state
     5  that holds a cache of the current value of these states. The job of a smart contract developer is to take an existing business 
     6  process that might govern financial prices or delivery conditions, and express it as a smart contract in a programming language
     7  
     8  Smart contracts primarily put, get and delete states in the world state, and can also query the state change history.
     9  Chaincode “shim” APIs implements [ChaincodeStubInterface](https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStubInterface) 
    10  which contain methods for access and modify the ledger, and to make invocations between chaincodes. Main methods are:
    11  
    12  * `GetState(key string) ([]byte, error)` performs a query to retrieve information about the current state of a business object
    13  
    14  * `PutState(key string, value []byte) error` creates a new business object or modifies an existing one in the ledger world state
    15  
    16  * `DelState(key string) error` removes of a business object from the current state of the ledger, but not its history
    17  
    18  * `GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error)` 
    19     queries the state in the ledger based on given partial composite key
    20     
    21  * `GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error)` returns a history of key values across time.
    22  
    23  
    24  All this methods use string key as record identifier and slice of bytes as state value. Most of examples uses JSON documents as 
    25  chaincode state value. Hyperledger Fabric supports both LevelDB as CouchDB to serve as state database, holding the latest state of each object.
    26  LevelDB is the default key-value state database embedded in every peer. CouchDB is an optional alternative external 
    27  state database with more features - it supports rich queries against JSON documents in chaincode state, whereas LevelDB only supports
    28   queries against keys.
    29   
    30  
    31  ## Querying and updating state with ChaincodeStubInterface methods
    32  
    33  As shown in many examples, assets can be represented as complex structures - Golang structs. 
    34  The chaincode itself can store data as a string in a key/value pair setup. Thus, we need to marshal struct to JSON string 
    35  before putting into chaincode state and unmarshal after getting from state.
    36  
    37  With `ChaincodeStubInterface` methods these operations looks like 
    38  [this](https://github.com/IBM/build-blockchain-insurance-app/blob/master/web/chaincode/src/bcins/invoke_insurance.go):
    39  
    40  ```go
    41      ct := ContractType{}
    42      
    43      err := json.Unmarshal([]byte(args[0]), &req)
    44      if err != nil {
    45          return shim.Error(err.Error())
    46      }
    47      
    48      key, err := stub.CreateCompositeKey(prefixContractType, []string{req.UUID})
    49      if err != nil {
    50          return shim.Error(err.Error())
    51      }
    52      
    53      valAsBytes, err := stub.GetState(key)
    54      if err != nil {
    55          return shim.Error(err.Error())
    56      }
    57      if len(valAsBytes) == 0 {
    58          return shim.Error("Contract Type could not be found")
    59      }
    60      err = json.Unmarshal(valAsBytes, &ct)
    61      if err != nil {
    62          return shim.Error(err.Error())
    63      }
    64      
    65      ct.Active = req.Active
    66      
    67      valAsBytes, err = json.Marshal(ct)
    68      if err != nil {
    69          return shim.Error(err.Error())
    70      }
    71      
    72      err = stub.PutState(key, valAsBytes)
    73      if err != nil {
    74          return shim.Error(err.Error())
    75      }
    76      
    77      return shim.Success(nil)
    78  ```
    79  
    80  In the example above smart contract code explicitly performs many auxiliary actions:
    81  
    82  * Creating composite key 
    83  * Unmarshaling data after receiving it from state
    84  * Marshaling data before placing it to state
    85  
    86  ## Modelling chaincode state with CCKit
    87  
    88  ### State methods wrapper
    89  
    90  CCKit contains [wrapper](state.go) on `ChaincodeStubInterface` methods to working with chaincode state. This methods
    91  simplifies chaincode key creation and data transformation during working with chaincode state.
    92  
    93  ```go
    94  type State interface {
    95      // Get returns value from state, converted to target type
    96      // entry can be Key (string or []string) or type implementing Keyer interface
    97      Get(entry interface{}, target ...interface{}) (result interface{}, err error)
    98      
    99      // Get returns value from state, converted to int
   100      // entry can be Key (string or []string) or type implementing Keyer interface
   101      GetInt(entry interface{}, defaultValue int) (result int, err error)
   102      
   103      // GetHistory returns slice of history records for entry, with values converted to target type
   104      // entry can be Key (string or []string) or type implementing Keyer interface
   105      GetHistory(entry interface{}, target interface{}) (result HistoryEntryList, err error)
   106      
   107      // Exists returns entry existence in state 
   108      // entry can be Key (string or []string) or type implementing Keyer interface
   109      Exists(entry interface{}) (exists bool, err error)
   110      
   111      // Put returns result of putting entry to state
   112      // entry can be Key (string or []string) or type implementing Keyer interface
   113      // if entry is implements Keyer interface and it's struct or type implementing
   114      // ToByter interface value can be omitted
   115      Put(entry interface{}, value ...interface{}) (err error)
   116      
   117      // Insert returns result of inserting entry to state
   118      // If same key exists in state error wil be returned
   119      // entry can be Key (string or []string) or type implementing Keyer interface
   120      // if entry is implements Keyer interface and it's struct or type implementing
   121      // ToByter interface value can be omitted
   122      Insert(entry interface{}, value ...interface{}) (err error)
   123      
   124      // List returns slice of target type
   125      // namespace can be part of key (string or []string) or entity with defined mapping
   126      List(namespace interface{}, target ...interface{}) (result []interface{}, err error)
   127      
   128      // Delete returns result of deleting entry from state
   129      // entry can be Key (string or []string) or type implementing Keyer interface
   130      Delete(entry interface{}) (err error)
   131  
   132  	...
   133  }
   134  ``` 
   135  
   136  ### Converting from/to bytes while operating with chaincode state
   137  
   138  State wrapper allows to automatically marshal golang type to/from slice of bytes. This type can be:
   139  
   140  * Any type implementing [ToByter and FromByter](../convert/convert.go) interface
   141  
   142  ```go
   143  type (
   144  	// FromByter interface supports FromBytes func for converting from slice of bytes to target type
   145  	FromByter interface {
   146  		FromBytes([]byte) (interface{}, error)
   147  	}
   148  
   149  	// ToByter interface supports ToBytes func for converting to slice of bytes from source type
   150  	ToByter interface {
   151  		ToBytes() ([]byte, error)
   152  	}
   153  )
   154  ```
   155  
   156  * Golang struct or one of supported types ( `int`, `string`, `[]string`)
   157  * [Protobuf](https://developers.google.com/protocol-buffers/docs/gotutorial) golang struct
   158  
   159  
   160  Golang structs [automatically](../convert) marshals/ unmarshals using [json.Marshal](https://golang.org/pkg/encoding/json/#Marshal) and
   161  and [json.Umarshal](https://golang.org/pkg/encoding/json/#Unmarshal) methods. 
   162  [proto.Marshal](https://godoc.org/github.com/golang/protobuf/proto#Marshal) and 
   163  [proto.Unmarshal](https://godoc.org/github.com/golang/protobuf/proto#Unmarshal) is used to convert protobuf.
   164  
   165  ### Creating state keys
   166  
   167  In the chaincode data model we often need to store many instances of one type on the ledger, such as multiple commercial papers,
   168  letters of credit, and so on.  In this case, the keys of those instances will be typically constructed from a combination of attributes—
   169  for example:
   170  
   171  > `CommercialPaper` + {Issuer} + {PaperId}
   172  
   173  yielding series of chaincode state entries keys [ `CommercialPaperIssuer1Id1`, `CommercialPaperIssuer2Id2`, ...]
   174  
   175  
   176  The logic of creation primary key of an instance can be customized in the code, or API functions can be provided in SHIM to
   177  construct a composite key (in other words, a unique key) of an instance based on
   178  a combination of several attributes. Composite keys can then be used as a normal string key to
   179  record and retrieve values using the PutState() and GetState() functions.
   180  
   181  The following snippet shows a list of functions that create and work with composite keys:
   182  
   183  ```go
   184  // The function creates a key by combining the attributes into a single string.
   185  // The arguments must be valid utf8 strings and must not contain U+0000 (nil byte) and U+10FFFF charactres.
   186  func CreateCompositeKey(objectType string, attributes []string) (string, error)
   187  
   188  // The function splits the compositeKey into attributes from which the key was formed.
   189  // This function is useful for extracting attributes from keys returned by range queries.
   190  func SplitCompositeKey(compositeKey string) (string, []string, error)
   191  ````
   192  
   193  When putting or getting data to/from chaincode state you must provide key. CCKit have 3 options for dealing with entries key:
   194   
   195  * Key can be passed explicit:
   196  
   197  ```go
   198  c.State().Put ( `my-key`, &myStructInstance)
   199  ```
   200  
   201  * Key type can implement [Keyer](state.go) interface 
   202  
   203  ```go
   204      Key []string
   205  
   206      // Keyer interface for entity containing logic of its key creation
   207      Keyer interface {
   208          Key() (Key, error)
   209      }
   210  ````
   211  
   212  `Key` type - is essentially slice of string, this slice will be automatically converted to string using `shim.CreateCompositeKey` method.
   213  
   214  and in chaincode you need to provide only type instance
   215  ```go
   216  c.State().Put (&myStructInstance)
   217  ```
   218  
   219  * Type can have associate mapping
   220  
   221  Mapping defines rules for namespace, primary and other key creation. Mapping mainly used with `protobuf` state schema.
   222  
   223  ### Range queries
   224  
   225  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. 
   226  Moreover, composite keys can be modeled to enable queries against multiple components of the key.
   227  
   228  The range functions return an iterator (StateQueryIteratorInterface) over a set of keys matching the query criteria. The returned keys are in lexical order. 
   229  Additionally, when a composite key has multiple attributes, the range query function, `GetStateByPartialCompositeKey()`, can be used to search for keys matching a
   230  subset of the attributes.
   231  
   232  For example, the key of a `CommercialPaper` composed of `Issuer` and `PaperId` attributes can be searched for entries only from one Issuer.
   233    
   234  ## Protobuf state example
   235  
   236  This example uses [Commercial paper scenario](https://hyperledger-fabric.readthedocs.io/en/release-1.4/developapps/scenario.html) and
   237  implements same functionality as [Node.JS](https://github.com/hyperledger/fabric-samples/tree/release-1.4/commercial-paper/organization/digibank/contract) 
   238  chaincode sample from official documentation. Example code located [here](../examples/cpaper).
   239  
   240  
   241  Protobuf schema advantages:
   242  
   243  1. Schema abstraction layer
   244  
   245  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, 
   246  and that the boundaries you create enforce your business rules.
   247  
   248  2. Extensions - validators etc
   249  
   250  Protobuf v3 does not support validating required parameters, but there are third party projects for proto validation, for example
   251  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.
   252  
   253  3. Easy Language Interoperability
   254  Because Protocol Buffers are implemented in a variety of languages, they make interoperability between polyglot applications in your architecture that much simpler. 
   255  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
   256  have guarantees about the safety and interoperability between those architectures. 
   257  
   258  
   259  
   260  ### Defining model
   261  
   262  Protobuf (short for Protocol buffers) is a way of encoding structured data in an efficient and extensible format. 
   263  With protocol buffers, you write a .proto description of the data structure you wish to store. 
   264  From that, the protocol buffer compiler creates a golang struct  (or ant) that implements automatic encoding and parsing 
   265  of the protocol buffer data with an efficient binary format. The generated class provides getters and setters 
   266  for the fields that make up a protocol buffer and takes care of the details of reading and writing the protocol
   267   buffer as a unit.
   268  
   269  In `Commercial Paper` example first we define messages, that will be stored in chaincode state or as events:
   270  
   271  * `CommercialPaper` will be stored in chaincode state
   272  * `CommercialPaperId` defines unique id part of commercial paper message
   273  * `IssueCommercialPaper` payload for `issue` transaction and event triggered when new commercial paper issued
   274  * `BuyCommercialPaper` payload for `buy` transaction and event triggered when commercial paper change owner
   275  * `RedeemCommercialPaper` payload for `redeem` transaction and event triggered when commercial paper redeemed
   276  
   277  ```proto
   278  syntax = "proto3";
   279  package schema;
   280  
   281  import "google/protobuf/timestamp.proto";
   282  
   283  message CommercialPaper {
   284  
   285      enum State {
   286          ISSUED = 0;
   287          TRADING = 1;
   288          REDEEMED = 2;
   289      }
   290  
   291      string issuer = 1;
   292      string paper_number = 2;
   293      string owner = 3;
   294      google.protobuf.Timestamp issue_date = 4;
   295      google.protobuf.Timestamp maturity_date = 5;
   296      int32 face_value = 6;
   297      State state = 7;
   298  }
   299  
   300  // CommercialPaperId identifier part
   301  message CommercialPaperId {
   302      string issuer = 1;
   303      string paper_number = 2;
   304  }
   305  
   306  // IssueCommercialPaper event
   307  message IssueCommercialPaper {
   308      string issuer = 1;
   309      string paper_number = 2;
   310      google.protobuf.Timestamp issue_date = 3;
   311      google.protobuf.Timestamp maturity_date = 4;
   312      int32 face_value = 5;
   313  }
   314  
   315  // BuyCommercialPaper event
   316  message BuyCommercialPaper {
   317      string issuer = 1;
   318      string paper_number = 2;
   319      string current_owner = 3;
   320      string new_owner = 4;
   321      int32 price = 5;
   322      google.protobuf.Timestamp purchase_date = 6;
   323  }
   324  
   325  // RedeemCommercialPaper event
   326  message RedeemCommercialPaper {
   327      string issuer = 1;
   328      string paper_number = 2;
   329      string redeeming_owner = 3;
   330      google.protobuf.Timestamp redeem_date = 4;
   331  }
   332  ```
   333  
   334  ### Defining protobuf to chaincode state mapping 
   335  
   336  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:
   337  
   338  * Primary key creation logic
   339  * Namespace logic
   340  * Secondary key creation logic
   341  
   342  For example, in definition below, we defined thant `schema.CommercialPaper` mapped to chaincode state with key attributes
   343  from `schema.CommercialPaperId` message (`Issuer`, `PaperNumber`). Also we define event
   344  
   345  ```go
   346  var (
   347      // State mappings
   348     	StateMappings = m.StateMappings{}.
   349     		//key namespace will be <`CommercialPaper`, Issuer, PaperNumber>
   350     		Add(&schema.CommercialPaper{}, m.PKeySchema(&schema.CommercialPaperId{}))
   351     
   352     	// EventMappings
   353     	EventMappings = m.EventMappings{}.
   354     		// event name will be `IssueCommercialPaper`,  payload - same as issue payload
   355     		Add(&schema.IssueCommercialPaper{}).
   356     		Add(&schema.BuyCommercialPaper{}).
   357     		Add(&schema.RedeemCommercialPaper{})
   358  )
   359  ```
   360  
   361  ### Chaincode
   362  
   363  ```go
   364  package cpaper
   365  
   366  import (
   367  	"fmt"
   368  	
   369  	"github.com/pkg/errors"
   370  	"github.com/s7techlab/cckit/examples/cpaper/schema"
   371  	"github.com/s7techlab/cckit/extensions/debug"
   372  	"github.com/s7techlab/cckit/extensions/encryption"
   373  	"github.com/s7techlab/cckit/extensions/owner"
   374  	"github.com/s7techlab/cckit/router"
   375  	"github.com/s7techlab/cckit/router/param/defparam"
   376  	m "github.com/s7techlab/cckit/state/mapping"
   377  )
   378  
   379  
   380  func NewCC() *router.Chaincode {
   381  
   382  	r := router.New(`commercial_paper`)
   383  
   384  	// Mappings for chaincode state
   385  	r.Use(m.MapStates(StateMappings))
   386  
   387  	// Mappings for chaincode events
   388  	r.Use(m.MapEvents(EventMappings))
   389  
   390  	// store in chaincode state information about chaincode first instantiator
   391  	r.Init(owner.InvokeSetFromCreator)
   392  
   393  	// method for debug chaincode state
   394  	debug.AddHandlers(r, `debug`, owner.Only)
   395  
   396  	r.
   397  		// read methods
   398  		Query(`list`, cpaperList).
   399  
   400  		// Get method has 2 params - commercial paper primary key components
   401  		Query(`get`, cpaperGet, defparam.Proto(&schema.CommercialPaperId{})).
   402  
   403  		// txn methods
   404  		Invoke(`issue`, cpaperIssue, defparam.Proto(&schema.IssueCommercialPaper{})).
   405  		Invoke(`buy`, cpaperBuy, defparam.Proto(&schema.BuyCommercialPaper{})).
   406  		Invoke(`redeem`, cpaperRedeem, defparam.Proto(&schema.RedeemCommercialPaper{})).
   407  		Invoke(`delete`, cpaperDelete, defparam.Proto(&schema.CommercialPaperId{}))
   408  
   409  	return router.NewChaincode(r)
   410  }
   411  
   412  
   413  func cpaperList(c router.Context) (interface{}, error) {
   414  	// commercial paper key is composite key <`CommercialPaper`>, {Issuer}, {PaperNumber} >
   415  	// where `CommercialPaper` - namespace of this type
   416  	// list method retrieves entries from chaincode state 
   417  	// using GetStateByPartialCompositeKey method, then unmarshal received from state bytes via proto.Ummarshal method
   418  	// and creates slice of *schema.CommercialPaper
   419  	return c.State().List(&schema.CommercialPaper{})
   420  }
   421  
   422  func cpaperIssue(c router.Context) (interface{}, error) {
   423  	var (
   424  		issue  = c.Param().(*schema.IssueCommercialPaper) //default parameter
   425  		cpaper = &schema.CommercialPaper{
   426  			Issuer:       issue.Issuer,
   427  			PaperNumber:  issue.PaperNumber,
   428  			Owner:        issue.Issuer,
   429  			IssueDate:    issue.IssueDate,
   430  			MaturityDate: issue.MaturityDate,
   431  			FaceValue:    issue.FaceValue,
   432  			State:        schema.CommercialPaper_ISSUED, // initial state
   433  		}
   434  		err error
   435  	)
   436  
   437  	if err = c.Event().Set(issue); err != nil {
   438  		return nil, err
   439  	}
   440  
   441  	return cpaper, c.State().Insert(cpaper)
   442  }
   443  
   444  func cpaperBuy(c router.Context) (interface{}, error) {
   445  
   446  	var (
   447  		cpaper *schema.CommercialPaper
   448  
   449  		// but tx payload
   450  		buy = c.Param().(*schema.BuyCommercialPaper)
   451  
   452  		// current commercial paper state
   453  		cp, err = c.State().Get(&schema.CommercialPaper{
   454  			Issuer:      buy.Issuer,
   455  			PaperNumber: buy.PaperNumber}, &schema.CommercialPaper{})
   456  	)
   457  
   458  	if err != nil {
   459  		return nil, errors.Wrap(err, `not found`)
   460  	}
   461  	cpaper = cp.(*schema.CommercialPaper)
   462  
   463  	// Validate current owner
   464  	if cpaper.Owner != buy.CurrentOwner {
   465  		return nil, fmt.Errorf(`paper %s %s is not owned by %s`, cpaper.Issuer, cpaper.PaperNumber, buy.CurrentOwner)
   466  	}
   467  
   468  	// First buy moves state from ISSUED to TRADING
   469  	if cpaper.State == schema.CommercialPaper_ISSUED {
   470  		cpaper.State = schema.CommercialPaper_TRADING
   471  	}
   472  
   473  	// Check paper is not already REDEEMED
   474  	if cpaper.State == schema.CommercialPaper_TRADING {
   475  		cpaper.Owner = buy.NewOwner
   476  	} else {
   477  		return nil, fmt.Errorf(`paper %s %s is not trading.current state = %s`, cpaper.Issuer, cpaper.PaperNumber, cpaper.State)
   478  	}
   479  
   480      if err = c.Event().Set(buy); err != nil {
   481  		return nil, err
   482  	}
   483  	return cpaper, c.State().Put(cpaper)
   484  }
   485  
   486  func cpaperRedeem(c router.Context) (interface{}, error) {
   487  	
   488  	// implement me
   489  
   490  	return nil, nil
   491  }
   492  
   493  func cpaperGet(c router.Context) (interface{}, error) {
   494  	return c.State().Get(c.Param().(*schema.CommercialPaperId))
   495  }
   496  
   497  func cpaperDelete(c router.Context) (interface{}, error) {
   498  	return nil, c.State().Delete(c.Param().(*schema.CommercialPaperId))
   499  }
   500  ```
   501  
   502  ### Tests
   503  We can [test](mapping/mapping_test.go) all chaincode use case scenarios using [MockStub](../testing)
   504  
   505  ```go
   506  package mapping_test
   507  
   508  import (
   509  	"testing"
   510  
   511  	"github.com/hyperledger/fabric/protos/peer"
   512  
   513  	"github.com/golang/protobuf/ptypes"
   514  
   515  	"github.com/golang/protobuf/proto"
   516  	"github.com/s7techlab/cckit/examples/cpaper/schema"
   517  	"github.com/s7techlab/cckit/examples/cpaper/testdata"
   518  	"github.com/s7techlab/cckit/state"
   519  
   520  	"github.com/s7techlab/cckit/examples/cpaper"
   521  
   522  	examplecert "github.com/s7techlab/cckit/examples/cert"
   523  	"github.com/s7techlab/cckit/identity"
   524  	testcc "github.com/s7techlab/cckit/testing"
   525  	expectcc "github.com/s7techlab/cckit/testing/expect"
   526  
   527  	. "github.com/onsi/ginkgo"
   528  	. "github.com/onsi/gomega"
   529  )
   530  
   531  func TestState(t *testing.T) {
   532  	RegisterFailHandler(Fail)
   533  	RunSpecs(t, "State suite")
   534  }
   535  
   536  var (
   537  	actors   identity.Actors
   538  	cPaperCC *testcc.MockStub
   539  	err      error
   540  )
   541  var _ = Describe(`Mapping`, func() {
   542  
   543  	BeforeSuite(func() {
   544  		actors, err = identity.ActorsFromPemFile(`SOME_MSP`, map[string]string{
   545  			`owner`: `s7techlab.pem`,
   546  		}, examplecert.Content)
   547  
   548  		Expect(err).To(BeNil())
   549  
   550  		//Create commercial papers chaincode mock - protobuf based schema
   551  		cPaperCC = testcc.NewMockStub(`cpapers`, cpaper.NewCC())
   552  		cPaperCC.From(actors[`owner`]).Init()
   553  
   554  	})
   555  
   556  	Describe(`Protobuf based schema`, func() {
   557  		It("Allow to add data to chaincode state", func(done Done) {
   558  
   559  			events := cPaperCC.EventSubscription()
   560  			expectcc.ResponseOk(cPaperCC.Invoke(`issue`, &testdata.CPapers[0]))
   561  
   562  			Expect(<-events).To(BeEquivalentTo(&peer.ChaincodeEvent{
   563  				EventName: `IssueCommercialPaper`,
   564  				Payload:   testcc.MustProtoMarshal(&testdata.CPapers[0]),
   565  			}))
   566  
   567  			expectcc.ResponseOk(cPaperCC.Invoke(`issue`, &testdata.CPapers[1]))
   568  			expectcc.ResponseOk(cPaperCC.Invoke(`issue`, &testdata.CPapers[2]))
   569  
   570  			close(done)
   571  		}, 0.2)
   572  
   573  		It("Disallow to insert entries with same keys", func() {
   574  			expectcc.ResponseError(cPaperCC.Invoke(`issue`, &testdata.CPapers[0]))
   575  		})
   576  
   577  		It("Allow to get entry list", func() {
   578  			cpapers := expectcc.PayloadIs(cPaperCC.Query(`list`), &[]schema.CommercialPaper{}).([]schema.CommercialPaper)
   579  			Expect(len(cpapers)).To(Equal(3))
   580  			Expect(cpapers[0].Issuer).To(Equal(testdata.CPapers[0].Issuer))
   581  			Expect(cpapers[0].PaperNumber).To(Equal(testdata.CPapers[0].PaperNumber))
   582  		})
   583  
   584  		It("Allow to get entry raw protobuf", func() {
   585  			cp := testdata.CPapers[0]
   586  			cpaperProtoFromCC := cPaperCC.Query(`get`, &schema.CommercialPaperId{Issuer: cp.Issuer, PaperNumber: cp.PaperNumber}).Payload
   587  
   588  			stateCpaper := &schema.CommercialPaper{
   589  				Issuer:       cp.Issuer,
   590  				PaperNumber:  cp.PaperNumber,
   591  				Owner:        cp.Issuer,
   592  				IssueDate:    cp.IssueDate,
   593  				MaturityDate: cp.MaturityDate,
   594  				FaceValue:    cp.FaceValue,
   595  				State:        schema.CommercialPaper_ISSUED, // initial state
   596  			}
   597  			cPaperProto, _ := proto.Marshal(stateCpaper)
   598  			Expect(cpaperProtoFromCC).To(Equal(cPaperProto))
   599  		})
   600  
   601  		It("Allow update data in chaincode state", func() {
   602  			cp := testdata.CPapers[0]
   603  			expectcc.ResponseOk(cPaperCC.Invoke(`buy`, &schema.BuyCommercialPaper{
   604  				Issuer:       cp.Issuer,
   605  				PaperNumber:  cp.PaperNumber,
   606  				CurrentOwner: cp.Issuer,
   607  				NewOwner:     `some-new-owner`,
   608  				Price:        cp.FaceValue - 10,
   609  				PurchaseDate: ptypes.TimestampNow(),
   610  			}))
   611  
   612  			cpaperFromCC := expectcc.PayloadIs(
   613  				cPaperCC.Query(`get`, &schema.CommercialPaperId{Issuer: cp.Issuer, PaperNumber: cp.PaperNumber}),
   614  				&schema.CommercialPaper{}).(*schema.CommercialPaper)
   615  
   616  			// state is updated
   617  			Expect(cpaperFromCC.State).To(Equal(schema.CommercialPaper_TRADING))
   618  			Expect(cpaperFromCC.Owner).To(Equal(`some-new-owner`))
   619  		})
   620  
   621  		It("Allow to delete entry", func() {
   622  
   623  			cp := testdata.CPapers[0]
   624  			toDelete := &schema.CommercialPaperId{Issuer: cp.Issuer, PaperNumber: cp.PaperNumber}
   625  
   626  			expectcc.ResponseOk(cPaperCC.Invoke(`delete`, toDelete))
   627  			cpapers := expectcc.PayloadIs(cPaperCC.Invoke(`list`), &[]schema.CommercialPaper{}).([]schema.CommercialPaper)
   628  
   629  			Expect(len(cpapers)).To(Equal(2))
   630  			expectcc.ResponseError(cPaperCC.Invoke(`get`, toDelete), state.ErrKeyNotFound)
   631  		})
   632  	})
   633  
   634  })
   635  ```