You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
757 lines
24 KiB
757 lines
24 KiB
// Copyright the Hyperledger Fabric contributors. All rights reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package shim
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"unicode/utf8"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/golang/protobuf/ptypes/timestamp"
|
|
"github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
|
|
pb "github.com/hyperledger/fabric-protos-go/peer"
|
|
)
|
|
|
|
// ChaincodeStub is an object passed to chaincode for shim side handling of
|
|
// APIs.
|
|
type ChaincodeStub struct {
|
|
TxID string
|
|
ChannelID string
|
|
chaincodeEvent *pb.ChaincodeEvent
|
|
args [][]byte
|
|
handler *Handler
|
|
signedProposal *pb.SignedProposal
|
|
proposal *pb.Proposal
|
|
validationParameterMetakey string
|
|
|
|
// Additional fields extracted from the signedProposal
|
|
creator []byte
|
|
transient map[string][]byte
|
|
binding []byte
|
|
|
|
decorations map[string][]byte
|
|
}
|
|
|
|
// ChaincodeInvocation functionality
|
|
|
|
func newChaincodeStub(handler *Handler, channelID, txid string, input *pb.ChaincodeInput, signedProposal *pb.SignedProposal) (*ChaincodeStub, error) {
|
|
stub := &ChaincodeStub{
|
|
TxID: txid,
|
|
ChannelID: channelID,
|
|
args: input.Args,
|
|
handler: handler,
|
|
signedProposal: signedProposal,
|
|
decorations: input.Decorations,
|
|
validationParameterMetakey: pb.MetaDataKeys_VALIDATION_PARAMETER.String(),
|
|
}
|
|
|
|
// TODO: sanity check: verify that every call to init with a nil
|
|
// signedProposal is a legitimate one, meaning it is an internal call
|
|
// to system chaincodes.
|
|
if signedProposal != nil {
|
|
var err error
|
|
|
|
stub.proposal = &pb.Proposal{}
|
|
err = proto.Unmarshal(signedProposal.ProposalBytes, stub.proposal)
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to extract Proposal from SignedProposal: %s", err)
|
|
}
|
|
|
|
// check for header
|
|
if len(stub.proposal.GetHeader()) == 0 {
|
|
return nil, errors.New("failed to extract Proposal fields: proposal header is nil")
|
|
}
|
|
|
|
// Extract creator, transient, binding...
|
|
hdr := &common.Header{}
|
|
if err := proto.Unmarshal(stub.proposal.GetHeader(), hdr); err != nil {
|
|
return nil, fmt.Errorf("failed to extract proposal header: %s", err)
|
|
}
|
|
|
|
// extract and validate channel header
|
|
chdr := &common.ChannelHeader{}
|
|
if err := proto.Unmarshal(hdr.ChannelHeader, chdr); err != nil {
|
|
return nil, fmt.Errorf("failed to extract channel header: %s", err)
|
|
}
|
|
validTypes := map[common.HeaderType]bool{
|
|
common.HeaderType_ENDORSER_TRANSACTION: true,
|
|
common.HeaderType_CONFIG: true,
|
|
}
|
|
if !validTypes[common.HeaderType(chdr.GetType())] {
|
|
return nil, fmt.Errorf(
|
|
"invalid channel header type. Expected %s or %s, received %s",
|
|
common.HeaderType_ENDORSER_TRANSACTION,
|
|
common.HeaderType_CONFIG,
|
|
common.HeaderType(chdr.GetType()),
|
|
)
|
|
}
|
|
|
|
// extract creator from signature header
|
|
shdr := &common.SignatureHeader{}
|
|
if err := proto.Unmarshal(hdr.GetSignatureHeader(), shdr); err != nil {
|
|
return nil, fmt.Errorf("failed to extract signature header: %s", err)
|
|
}
|
|
stub.creator = shdr.GetCreator()
|
|
|
|
// extract trasient data from proposal payload
|
|
payload := &pb.ChaincodeProposalPayload{}
|
|
if err := proto.Unmarshal(stub.proposal.GetPayload(), payload); err != nil {
|
|
return nil, fmt.Errorf("failed to extract proposal payload: %s", err)
|
|
}
|
|
stub.transient = payload.GetTransientMap()
|
|
|
|
// compute the proposal binding from the nonce, creator and epoch
|
|
epoch := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(epoch, chdr.GetEpoch())
|
|
digest := sha256.Sum256(append(append(shdr.GetNonce(), stub.creator...), epoch...))
|
|
stub.binding = digest[:]
|
|
|
|
}
|
|
|
|
return stub, nil
|
|
}
|
|
|
|
// GetTxID returns the transaction ID for the proposal
|
|
func (s *ChaincodeStub) GetTxID() string {
|
|
return s.TxID
|
|
}
|
|
|
|
// GetChannelID returns the channel for the proposal
|
|
func (s *ChaincodeStub) GetChannelID() string {
|
|
return s.ChannelID
|
|
}
|
|
|
|
// GetDecorations ...
|
|
func (s *ChaincodeStub) GetDecorations() map[string][]byte {
|
|
return s.decorations
|
|
}
|
|
|
|
// GetMSPID returns the local mspid of the peer by checking the CORE_PEER_LOCALMSPID
|
|
// env var and returns an error if the env var is not set
|
|
func GetMSPID() (string, error) {
|
|
mspid := os.Getenv("CORE_PEER_LOCALMSPID")
|
|
|
|
if mspid == "" {
|
|
return "", errors.New("'CORE_PEER_LOCALMSPID' is not set")
|
|
}
|
|
|
|
return mspid, nil
|
|
}
|
|
|
|
// ------------- Call Chaincode functions ---------------
|
|
|
|
// InvokeChaincode documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response {
|
|
// Internally we handle chaincode name as a composite name
|
|
if channel != "" {
|
|
chaincodeName = chaincodeName + "/" + channel
|
|
}
|
|
return s.handler.handleInvokeChaincode(chaincodeName, args, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// --------- State functions ----------
|
|
|
|
// GetState documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetState(key string) ([]byte, error) {
|
|
// Access public data by setting the collection to empty string
|
|
collection := ""
|
|
return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// SetStateValidationParameter documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) SetStateValidationParameter(key string, ep []byte) error {
|
|
return s.handler.handlePutStateMetadataEntry("", key, s.validationParameterMetakey, ep, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// GetStateValidationParameter documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetStateValidationParameter(key string) ([]byte, error) {
|
|
md, err := s.handler.handleGetStateMetadata("", key, s.ChannelID, s.TxID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ep, ok := md[s.validationParameterMetakey]; ok {
|
|
return ep, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// PutState documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) PutState(key string, value []byte) error {
|
|
if key == "" {
|
|
return errors.New("key must not be an empty string")
|
|
}
|
|
// Access public data by setting the collection to empty string
|
|
collection := ""
|
|
return s.handler.handlePutState(collection, key, value, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
func (s *ChaincodeStub) createStateQueryIterator(response *pb.QueryResponse) *StateQueryIterator {
|
|
return &StateQueryIterator{
|
|
CommonIterator: &CommonIterator{
|
|
handler: s.handler,
|
|
channelID: s.ChannelID,
|
|
txid: s.TxID,
|
|
response: response,
|
|
currentLoc: 0,
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetQueryResult documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetQueryResult(query string) (StateQueryIteratorInterface, error) {
|
|
// Access public data by setting the collection to empty string
|
|
collection := ""
|
|
// ignore QueryResponseMetadata as it is not applicable for a rich query without pagination
|
|
iterator, _, err := s.handleGetQueryResult(collection, query, nil)
|
|
|
|
return iterator, err
|
|
}
|
|
|
|
// DelState documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) DelState(key string) error {
|
|
// Access public data by setting the collection to empty string
|
|
collection := ""
|
|
return s.handler.handleDelState(collection, key, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// --------- private state functions ---------
|
|
|
|
// GetPrivateData documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetPrivateData(collection string, key string) ([]byte, error) {
|
|
if collection == "" {
|
|
return nil, fmt.Errorf("collection must not be an empty string")
|
|
}
|
|
return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// GetPrivateDataHash documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetPrivateDataHash(collection string, key string) ([]byte, error) {
|
|
if collection == "" {
|
|
return nil, fmt.Errorf("collection must not be an empty string")
|
|
}
|
|
return s.handler.handleGetPrivateDataHash(collection, key, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// PutPrivateData documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) PutPrivateData(collection string, key string, value []byte) error {
|
|
if collection == "" {
|
|
return fmt.Errorf("collection must not be an empty string")
|
|
}
|
|
if key == "" {
|
|
return fmt.Errorf("key must not be an empty string")
|
|
}
|
|
return s.handler.handlePutState(collection, key, value, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// DelPrivateData documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) DelPrivateData(collection string, key string) error {
|
|
if collection == "" {
|
|
return fmt.Errorf("collection must not be an empty string")
|
|
}
|
|
return s.handler.handleDelState(collection, key, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// GetPrivateDataByRange documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetPrivateDataByRange(collection, startKey, endKey string) (StateQueryIteratorInterface, error) {
|
|
if collection == "" {
|
|
return nil, fmt.Errorf("collection must not be an empty string")
|
|
}
|
|
if startKey == "" {
|
|
startKey = emptyKeySubstitute
|
|
}
|
|
if err := validateSimpleKeys(startKey, endKey); err != nil {
|
|
return nil, err
|
|
}
|
|
// ignore QueryResponseMetadata as it is not applicable for a range query without pagination
|
|
iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil)
|
|
|
|
return iterator, err
|
|
}
|
|
|
|
func (s *ChaincodeStub) createRangeKeysForPartialCompositeKey(objectType string, attributes []string) (string, string, error) {
|
|
partialCompositeKey, err := s.CreateCompositeKey(objectType, attributes)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
startKey := partialCompositeKey
|
|
endKey := partialCompositeKey + string(maxUnicodeRuneValue)
|
|
|
|
return startKey, endKey, nil
|
|
}
|
|
|
|
// GetPrivateDataByPartialCompositeKey documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetPrivateDataByPartialCompositeKey(collection, objectType string, attributes []string) (StateQueryIteratorInterface, error) {
|
|
if collection == "" {
|
|
return nil, fmt.Errorf("collection must not be an empty string")
|
|
}
|
|
|
|
startKey, endKey, err := s.createRangeKeysForPartialCompositeKey(objectType, attributes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// ignore QueryResponseMetadata as it is not applicable for a partial composite key query without pagination
|
|
iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil)
|
|
|
|
return iterator, err
|
|
}
|
|
|
|
// GetPrivateDataQueryResult documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetPrivateDataQueryResult(collection, query string) (StateQueryIteratorInterface, error) {
|
|
if collection == "" {
|
|
return nil, fmt.Errorf("collection must not be an empty string")
|
|
}
|
|
// ignore QueryResponseMetadata as it is not applicable for a range query without pagination
|
|
iterator, _, err := s.handleGetQueryResult(collection, query, nil)
|
|
|
|
return iterator, err
|
|
}
|
|
|
|
// GetPrivateDataValidationParameter documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetPrivateDataValidationParameter(collection, key string) ([]byte, error) {
|
|
md, err := s.handler.handleGetStateMetadata(collection, key, s.ChannelID, s.TxID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ep, ok := md[s.validationParameterMetakey]; ok {
|
|
return ep, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// SetPrivateDataValidationParameter documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) SetPrivateDataValidationParameter(collection, key string, ep []byte) error {
|
|
return s.handler.handlePutStateMetadataEntry(collection, key, s.validationParameterMetakey, ep, s.ChannelID, s.TxID)
|
|
}
|
|
|
|
// CommonIterator documentation can be found in interfaces.go
|
|
type CommonIterator struct {
|
|
handler *Handler
|
|
channelID string
|
|
txid string
|
|
response *pb.QueryResponse
|
|
currentLoc int
|
|
}
|
|
|
|
// StateQueryIterator documentation can be found in interfaces.go
|
|
type StateQueryIterator struct {
|
|
*CommonIterator
|
|
}
|
|
|
|
// HistoryQueryIterator documentation can be found in interfaces.go
|
|
type HistoryQueryIterator struct {
|
|
*CommonIterator
|
|
}
|
|
|
|
// General interface for supporting different types of query results.
|
|
// Actual types differ for different queries
|
|
type queryResult interface{}
|
|
|
|
type resultType uint8
|
|
|
|
// TODO: Document constants
|
|
/*
|
|
Constants ...
|
|
*/
|
|
const (
|
|
StateQueryResult resultType = iota + 1
|
|
HistoryQueryResult
|
|
)
|
|
|
|
func createQueryResponseMetadata(metadataBytes []byte) (*pb.QueryResponseMetadata, error) {
|
|
metadata := &pb.QueryResponseMetadata{}
|
|
err := proto.Unmarshal(metadataBytes, metadata)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return metadata, nil
|
|
}
|
|
|
|
func (s *ChaincodeStub) handleGetStateByRange(collection, startKey, endKey string,
|
|
metadata []byte) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
|
|
|
|
response, err := s.handler.handleGetStateByRange(collection, startKey, endKey, metadata, s.ChannelID, s.TxID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
iterator := s.createStateQueryIterator(response)
|
|
responseMetadata, err := createQueryResponseMetadata(response.Metadata)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return iterator, responseMetadata, nil
|
|
}
|
|
|
|
func (s *ChaincodeStub) handleGetQueryResult(collection, query string,
|
|
metadata []byte) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
|
|
|
|
response, err := s.handler.handleGetQueryResult(collection, query, metadata, s.ChannelID, s.TxID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
iterator := s.createStateQueryIterator(response)
|
|
responseMetadata, err := createQueryResponseMetadata(response.Metadata)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return iterator, responseMetadata, nil
|
|
}
|
|
|
|
// GetStateByRange documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) {
|
|
if startKey == "" {
|
|
startKey = emptyKeySubstitute
|
|
}
|
|
if err := validateSimpleKeys(startKey, endKey); err != nil {
|
|
return nil, err
|
|
}
|
|
collection := ""
|
|
|
|
// ignore QueryResponseMetadata as it is not applicable for a range query without pagination
|
|
iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil)
|
|
|
|
return iterator, err
|
|
}
|
|
|
|
// GetHistoryForKey documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) {
|
|
response, err := s.handler.handleGetHistoryForKey(key, s.ChannelID, s.TxID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &HistoryQueryIterator{CommonIterator: &CommonIterator{s.handler, s.ChannelID, s.TxID, response, 0}}, nil
|
|
}
|
|
|
|
//CreateCompositeKey documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) CreateCompositeKey(objectType string, attributes []string) (string, error) {
|
|
return CreateCompositeKey(objectType, attributes)
|
|
}
|
|
|
|
//SplitCompositeKey documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) SplitCompositeKey(compositeKey string) (string, []string, error) {
|
|
return splitCompositeKey(compositeKey)
|
|
}
|
|
|
|
// CreateCompositeKey ...
|
|
func CreateCompositeKey(objectType string, attributes []string) (string, error) {
|
|
if err := validateCompositeKeyAttribute(objectType); err != nil {
|
|
return "", err
|
|
}
|
|
ck := compositeKeyNamespace + objectType + string(minUnicodeRuneValue)
|
|
for _, att := range attributes {
|
|
if err := validateCompositeKeyAttribute(att); err != nil {
|
|
return "", err
|
|
}
|
|
ck += att + string(minUnicodeRuneValue)
|
|
}
|
|
return ck, nil
|
|
}
|
|
|
|
func splitCompositeKey(compositeKey string) (string, []string, error) {
|
|
componentIndex := 1
|
|
components := []string{}
|
|
for i := 1; i < len(compositeKey); i++ {
|
|
if compositeKey[i] == minUnicodeRuneValue {
|
|
components = append(components, compositeKey[componentIndex:i])
|
|
componentIndex = i + 1
|
|
}
|
|
}
|
|
return components[0], components[1:], nil
|
|
}
|
|
|
|
func validateCompositeKeyAttribute(str string) error {
|
|
if !utf8.ValidString(str) {
|
|
return fmt.Errorf("not a valid utf8 string: [%x]", str)
|
|
}
|
|
for index, runeValue := range str {
|
|
if runeValue == minUnicodeRuneValue || runeValue == maxUnicodeRuneValue {
|
|
return fmt.Errorf(`input contains unicode %#U starting at position [%d]. %#U and %#U are not allowed in the input attribute of a composite key`,
|
|
runeValue, index, minUnicodeRuneValue, maxUnicodeRuneValue)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//To ensure that simple keys do not go into composite key namespace,
|
|
//we validate simplekey to check whether the key starts with 0x00 (which
|
|
//is the namespace for compositeKey). This helps in avoding simple/composite
|
|
//key collisions.
|
|
func validateSimpleKeys(simpleKeys ...string) error {
|
|
for _, key := range simpleKeys {
|
|
if len(key) > 0 && key[0] == compositeKeyNamespace[0] {
|
|
return fmt.Errorf(`first character of the key [%s] contains a null character which is not allowed`, key)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//GetStateByPartialCompositeKey function can be invoked by a chaincode to query the
|
|
//state based on a given partial composite key. This function returns an
|
|
//iterator which can be used to iterate over all composite keys whose prefix
|
|
//matches the given partial composite key. This function should be used only for
|
|
//a partial composite key. For a full composite key, an iter with empty response
|
|
//would be returned.
|
|
func (s *ChaincodeStub) GetStateByPartialCompositeKey(objectType string, attributes []string) (StateQueryIteratorInterface, error) {
|
|
collection := ""
|
|
startKey, endKey, err := s.createRangeKeysForPartialCompositeKey(objectType, attributes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// ignore QueryResponseMetadata as it is not applicable for a partial composite key query without pagination
|
|
iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil)
|
|
|
|
return iterator, err
|
|
}
|
|
|
|
func createQueryMetadata(pageSize int32, bookmark string) ([]byte, error) {
|
|
// Construct the QueryMetadata with a page size and a bookmark needed for pagination
|
|
metadata := &pb.QueryMetadata{PageSize: pageSize, Bookmark: bookmark}
|
|
metadataBytes, err := proto.Marshal(metadata)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return metadataBytes, nil
|
|
}
|
|
|
|
// GetStateByRangeWithPagination ...
|
|
func (s *ChaincodeStub) GetStateByRangeWithPagination(startKey, endKey string, pageSize int32,
|
|
bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
|
|
|
|
if startKey == "" {
|
|
startKey = emptyKeySubstitute
|
|
}
|
|
if err := validateSimpleKeys(startKey, endKey); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
collection := ""
|
|
|
|
metadata, err := createQueryMetadata(pageSize, bookmark)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return s.handleGetStateByRange(collection, startKey, endKey, metadata)
|
|
}
|
|
|
|
// GetStateByPartialCompositeKeyWithPagination ...
|
|
func (s *ChaincodeStub) GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string,
|
|
pageSize int32, bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
|
|
|
|
collection := ""
|
|
|
|
metadata, err := createQueryMetadata(pageSize, bookmark)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
startKey, endKey, err := s.createRangeKeysForPartialCompositeKey(objectType, keys)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return s.handleGetStateByRange(collection, startKey, endKey, metadata)
|
|
}
|
|
|
|
// GetQueryResultWithPagination ...
|
|
func (s *ChaincodeStub) GetQueryResultWithPagination(query string, pageSize int32,
|
|
bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
|
|
// Access public data by setting the collection to empty string
|
|
collection := ""
|
|
|
|
metadata, err := createQueryMetadata(pageSize, bookmark)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return s.handleGetQueryResult(collection, query, metadata)
|
|
}
|
|
|
|
// Next ...
|
|
func (iter *StateQueryIterator) Next() (*queryresult.KV, error) {
|
|
result, err := iter.nextResult(StateQueryResult)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result.(*queryresult.KV), err
|
|
}
|
|
|
|
// Next ...
|
|
func (iter *HistoryQueryIterator) Next() (*queryresult.KeyModification, error) {
|
|
result, err := iter.nextResult(HistoryQueryResult)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result.(*queryresult.KeyModification), err
|
|
}
|
|
|
|
// HasNext documentation can be found in interfaces.go
|
|
func (iter *CommonIterator) HasNext() bool {
|
|
if iter.currentLoc < len(iter.response.Results) || iter.response.HasMore {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// getResultsFromBytes deserializes QueryResult and return either a KV struct
|
|
// or KeyModification depending on the result type (i.e., state (range/execute)
|
|
// query, history query). Note that queryResult is an empty golang
|
|
// interface that can hold values of any type.
|
|
func (iter *CommonIterator) getResultFromBytes(queryResultBytes *pb.QueryResultBytes,
|
|
rType resultType) (queryResult, error) {
|
|
|
|
if rType == StateQueryResult {
|
|
stateQueryResult := &queryresult.KV{}
|
|
if err := proto.Unmarshal(queryResultBytes.ResultBytes, stateQueryResult); err != nil {
|
|
return nil, fmt.Errorf("error unmarshaling result from bytes: %s", err)
|
|
}
|
|
return stateQueryResult, nil
|
|
|
|
} else if rType == HistoryQueryResult {
|
|
historyQueryResult := &queryresult.KeyModification{}
|
|
if err := proto.Unmarshal(queryResultBytes.ResultBytes, historyQueryResult); err != nil {
|
|
return nil, err
|
|
}
|
|
return historyQueryResult, nil
|
|
}
|
|
return nil, errors.New("wrong result type")
|
|
}
|
|
|
|
func (iter *CommonIterator) fetchNextQueryResult() error {
|
|
response, err := iter.handler.handleQueryStateNext(iter.response.Id, iter.channelID, iter.txid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
iter.currentLoc = 0
|
|
iter.response = response
|
|
return nil
|
|
}
|
|
|
|
// nextResult returns the next QueryResult (i.e., either a KV struct or KeyModification)
|
|
// from the state or history query iterator. Note that queryResult is an
|
|
// empty golang interface that can hold values of any type.
|
|
func (iter *CommonIterator) nextResult(rType resultType) (queryResult, error) {
|
|
if iter.currentLoc < len(iter.response.Results) {
|
|
// On valid access of an element from cached results
|
|
queryResult, err := iter.getResultFromBytes(iter.response.Results[iter.currentLoc], rType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
iter.currentLoc++
|
|
|
|
if iter.currentLoc == len(iter.response.Results) && iter.response.HasMore {
|
|
// On access of last item, pre-fetch to update HasMore flag
|
|
if err = iter.fetchNextQueryResult(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return queryResult, err
|
|
} else if !iter.response.HasMore {
|
|
// On call to Next() without check of HasMore
|
|
return nil, errors.New("no such key")
|
|
}
|
|
|
|
// should not fall through here
|
|
// case: no cached results but HasMore is true.
|
|
return nil, errors.New("invalid iterator state")
|
|
}
|
|
|
|
// Close documentation can be found in interfaces.go
|
|
func (iter *CommonIterator) Close() error {
|
|
_, err := iter.handler.handleQueryStateClose(iter.response.Id, iter.channelID, iter.txid)
|
|
return err
|
|
}
|
|
|
|
// GetArgs documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetArgs() [][]byte {
|
|
return s.args
|
|
}
|
|
|
|
// GetStringArgs documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetStringArgs() []string {
|
|
args := s.GetArgs()
|
|
strargs := make([]string, 0, len(args))
|
|
for _, barg := range args {
|
|
strargs = append(strargs, string(barg))
|
|
}
|
|
return strargs
|
|
}
|
|
|
|
// GetFunctionAndParameters documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetFunctionAndParameters() (function string, params []string) {
|
|
allargs := s.GetStringArgs()
|
|
function = ""
|
|
params = []string{}
|
|
if len(allargs) >= 1 {
|
|
function = allargs[0]
|
|
params = allargs[1:]
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetCreator documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetCreator() ([]byte, error) {
|
|
return s.creator, nil
|
|
}
|
|
|
|
// GetTransient documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetTransient() (map[string][]byte, error) {
|
|
return s.transient, nil
|
|
}
|
|
|
|
// GetBinding documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetBinding() ([]byte, error) {
|
|
return s.binding, nil
|
|
}
|
|
|
|
// GetSignedProposal documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetSignedProposal() (*pb.SignedProposal, error) {
|
|
return s.signedProposal, nil
|
|
}
|
|
|
|
// GetArgsSlice documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetArgsSlice() ([]byte, error) {
|
|
args := s.GetArgs()
|
|
res := []byte{}
|
|
for _, barg := range args {
|
|
res = append(res, barg...)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// GetTxTimestamp documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) GetTxTimestamp() (*timestamp.Timestamp, error) {
|
|
hdr := &common.Header{}
|
|
if err := proto.Unmarshal(s.proposal.Header, hdr); err != nil {
|
|
return nil, fmt.Errorf("error unmarshaling Header: %s", err)
|
|
}
|
|
|
|
chdr := &common.ChannelHeader{}
|
|
if err := proto.Unmarshal(hdr.ChannelHeader, chdr); err != nil {
|
|
return nil, fmt.Errorf("error unmarshaling ChannelHeader: %s", err)
|
|
}
|
|
|
|
return chdr.GetTimestamp(), nil
|
|
}
|
|
|
|
// ------------- ChaincodeEvent API ----------------------
|
|
|
|
// SetEvent documentation can be found in interfaces.go
|
|
func (s *ChaincodeStub) SetEvent(name string, payload []byte) error {
|
|
if name == "" {
|
|
return errors.New("event name can not be empty string")
|
|
}
|
|
s.chaincodeEvent = &pb.ChaincodeEvent{EventName: name, Payload: payload}
|
|
return nil
|
|
}
|