Skip to content

Commit

Permalink
Chaincode API Enhancement
Browse files Browse the repository at this point in the history
This change-set allows the chaincode to get the
proposal's creator, transient field and binding.
This is done by trasferring to the chiancode the proposal.

This change-set comes in the context of
https://jira.hyperledger.org/browse/FAB-1751
and
https://jira.hyperledger.org/browse/FAB-1752

Change-Id: I8ac643a24008fbe7edf2779636dc0de1d1a0ddc1
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Feb 19, 2017
1 parent dd658bf commit 458328b
Show file tree
Hide file tree
Showing 30 changed files with 796 additions and 683 deletions.
10 changes: 5 additions & 5 deletions accesscontrol/crypto/attr/attr_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import (

// chaincodeHolder is the struct that hold the certificate and the metadata. An implementation is ChaincodeStub
type chaincodeHolder interface {
// GetCallerCertificate returns caller certificate
GetCallerCertificate() ([]byte, error)
// GetCreator returns caller certificate
GetCreator() ([]byte, error)

// GetCallerMetadata returns caller metadata
/*
Expand Down Expand Up @@ -82,8 +82,8 @@ type chaincodeHolderImpl struct {
Certificate []byte
}

// GetCallerCertificate returns caller certificate
func (holderImpl *chaincodeHolderImpl) GetCallerCertificate() ([]byte, error) {
// GetCreator returns caller certificate
func (holderImpl *chaincodeHolderImpl) GetCreator() ([]byte, error) {
return holderImpl.Certificate, nil
}

Expand All @@ -99,7 +99,7 @@ func GetValueFrom(attributeName string, cert []byte) ([]byte, error) {
//NewAttributesHandlerImpl creates a new AttributesHandlerImpl from a pb.ChaincodeSecurityContext object.
func NewAttributesHandlerImpl(holder chaincodeHolder) (*AttributesHandlerImpl, error) {
// Getting certificate
certRaw, err := holder.GetCallerCertificate()
certRaw, err := holder.GetCreator()
if err != nil {
return nil, err
}
Expand Down
16 changes: 8 additions & 8 deletions accesscontrol/crypto/attr/attr_support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ type chaincodeStubMock struct {
*/
}

// GetCallerCertificate returns caller certificate
func (shim *chaincodeStubMock) GetCallerCertificate() ([]byte, error) {
// GetCreator returns caller certificate
func (shim *chaincodeStubMock) GetCreator() ([]byte, error) {
return shim.callerCert, nil
}

Expand All @@ -60,9 +60,9 @@ type certErrorMock struct {
*/
}

// GetCallerCertificate returns caller certificate
func (shim *certErrorMock) GetCallerCertificate() ([]byte, error) {
return nil, errors.New("GetCallerCertificate error")
// GetCreator returns caller certificate
func (shim *certErrorMock) GetCreator() ([]byte, error) {
return nil, errors.New("GetCreator error")
}

/*
Expand All @@ -76,16 +76,16 @@ type metadataErrorMock struct {
callerCert []byte
}

// GetCallerCertificate returns caller certificate
func (shim *metadataErrorMock) GetCallerCertificate() ([]byte, error) {
// GetCreator returns caller certificate
func (shim *metadataErrorMock) GetCreator() ([]byte, error) {
return shim.callerCert, nil
}

/*
TODO: ##attributes-keys-pending This code have be redefined to avoid use of metadata field.
// GetCallerMetadata returns caller metadata
func (shim *metadataErrorMock) GetCallerMetadata() ([]byte, error) {
return nil, errors.New("GetCallerCertificate error")
return nil, errors.New("GetCreator error")
}*/

func TestVerifyAttribute(t *testing.T) {
Expand Down
11 changes: 1 addition & 10 deletions core/chaincode/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
"github.com/looplab/fsm"
logging "github.com/op/go-logging"
"golang.org/x/net/context"
Expand Down Expand Up @@ -1379,13 +1378,7 @@ func (handler *Handler) setChaincodeProposal(prop *pb.Proposal, msg *pb.Chaincod
if prop != nil {
chaincodeLogger.Debug("Proposal different from nil. Creating chaincode proposal context...")

proposalContext, err := utils.GetChaincodeProposalContext(prop)
if err != nil {
chaincodeLogger.Debug("Failed getting proposal context from proposal [%s]", err)
return fmt.Errorf("Failed getting proposal context from proposal [%s]", err)
}

msg.ProposalContext = proposalContext
msg.Proposal = prop
}
return nil
}
Expand All @@ -1400,7 +1393,6 @@ func (handler *Handler) ready(ctxt context.Context, chainID string, txid string,
chaincodeLogger.Debug("sending READY")
ccMsg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_READY, Txid: txid}

//if security is disabled the context elements will just be nil
if err := handler.setChaincodeProposal(prop, ccMsg); err != nil {
return nil, err
}
Expand Down Expand Up @@ -1465,7 +1457,6 @@ func (handler *Handler) sendExecuteMessage(ctxt context.Context, chainID string,
chaincodeLogger.Debugf("[%s]Inside sendExecuteMessage. Message %s", shorttxid(msg.Txid), msg.Type.String())
}

//if security is disabled the context elements will just be nil
if err = handler.setChaincodeProposal(prop, msg); err != nil {
return nil, err
}
Expand Down
88 changes: 53 additions & 35 deletions core/chaincode/shim/chaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ import (

"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/comm"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"
"github.com/spf13/viper"
"golang.org/x/net/context"
Expand All @@ -49,11 +49,16 @@ const (
// ChaincodeStub is an object passed to chaincode for shim side handling of
// APIs.
type ChaincodeStub struct {
TxID string
proposalContext *pb.ChaincodeProposalContext
chaincodeEvent *pb.ChaincodeEvent
args [][]byte
handler *Handler
TxID string
chaincodeEvent *pb.ChaincodeEvent
args [][]byte
handler *Handler
proposal *pb.Proposal

// Additional fields extracted from the proposal
creator []byte
transient map[string][]byte
binding []byte
}

// Peer address derived from command line or env var
Expand Down Expand Up @@ -270,20 +275,32 @@ func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode
// -- init stub ---
// ChaincodeInvocation functionality

func (stub *ChaincodeStub) init(handler *Handler, txid string, input *pb.ChaincodeInput, proposalContext *pb.ChaincodeProposalContext) {
func (stub *ChaincodeStub) init(handler *Handler, txid string, input *pb.ChaincodeInput, proposal *pb.Proposal) error {
stub.TxID = txid
stub.args = input.Args
stub.handler = handler
stub.proposalContext = proposalContext
}
stub.proposal = proposal

// TODO: sanity check: verify that every call to init with a nil
// proposal is a legitimate one, meaning it is an internal call
// to system chaincodes.
if proposal != nil {
// Extract creator, transient, binding...
var err error
stub.creator, stub.transient, err = utils.GetChaincodeProposalContext(proposal)
if err != nil {
return fmt.Errorf("Failed extracting proposal fields. [%s]", err)
}

// InitTestStub initializes an appropriate stub for testing chaincode
func InitTestStub(funargs ...string) *ChaincodeStub {
stub := ChaincodeStub{}
allargs := util.ToChaincodeArgs(funargs...)
newCI := &pb.ChaincodeInput{Args: allargs}
stub.init(&Handler{}, "TEST-txid", newCI, nil) // TODO: add msg.ProposalContext
return &stub
// TODO: txid must uniquely identity the transaction.
// Remove this comment once replay attack protection will be in place
stub.binding, err = utils.ComputeProposalBinding(proposal)
if err != nil {
return fmt.Errorf("Failed computing binding from proposal. [%s]", err)
}
}

return nil
}

// GetTxID returns the transaction ID
Expand Down Expand Up @@ -505,33 +522,34 @@ func (stub *ChaincodeStub) GetFunctionAndParameters() (function string, params [
return
}

// GetCallerCertificate returns caller certificate
func (stub *ChaincodeStub) GetCallerCertificate() ([]byte, error) {
if stub.proposalContext != nil {
return stub.proposalContext.Transient, nil
}

return nil, errors.New("Creator field not set.")
// GetCreator returns SignatureHeader.Creator of the proposal
// this Stub refers to.
func (stub *ChaincodeStub) GetCreator() ([]byte, error) {
return stub.creator, nil
}

// GetCallerMetadata returns caller metadata
func (stub *ChaincodeStub) GetCallerMetadata() ([]byte, error) {
if stub.proposalContext != nil {
return stub.proposalContext.Transient, nil
}

return nil, errors.New("Transient field not set.")
// GetTransient returns the ChaincodeProposalPayload.transient field.
// It is a map that contains data (e.g. cryptographic material)
// that might be used to implement some form of application-level confidentiality. The contents
// of this field, as prescribed by ChaincodeProposalPayload, are supposed to always
// be omitted from the transaction and excluded from the ledger.
func (stub *ChaincodeStub) GetTransient() (map[string][]byte, error) {
return stub.transient, nil
}

// GetBinding returns the transaction binding
func (stub *ChaincodeStub) GetBinding() ([]byte, error) {
return nil, nil
return stub.binding, nil
}

// GetPayload returns transaction payload, which is a `ChaincodeSpec` defined
// in fabric/protos/chaincode.proto
func (stub *ChaincodeStub) GetPayload() ([]byte, error) {
return nil, nil
// GetArgsSlice returns the arguments to the stub call as a byte array
func (stub *ChaincodeStub) GetArgsSlice() ([]byte, error) {
args := stub.GetArgs()
res := []byte{}
for _, barg := range args {
res = append(res, barg...)
}
return res, nil
}

// GetTxTimestamp returns transaction created timestamp, which is currently
Expand Down
16 changes: 14 additions & 2 deletions core/chaincode/shim/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,12 @@ func (handler *Handler) handleInit(msg *pb.ChaincodeMessage) {
// Call chaincode's Run
// Create the ChaincodeStub which the chaincode can use to callback
stub := new(ChaincodeStub)
stub.init(handler, msg.Txid, input, msg.ProposalContext)
err := stub.init(handler, msg.Txid, input, msg.Proposal)
if err != nil {
chaincodeLogger.Errorf("[%s]Init get error response [%s]. Sending %s", shorttxid(msg.Txid), err.Error(), pb.ChaincodeMessage_ERROR)
nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: []byte(err.Error()), Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent}
return
}
res := handler.cc.Init(stub)
chaincodeLogger.Debugf("[%s]Init get response status: %d", shorttxid(msg.Txid), res.Status)

Expand Down Expand Up @@ -296,7 +301,14 @@ func (handler *Handler) handleTransaction(msg *pb.ChaincodeMessage) {
// Call chaincode's Run
// Create the ChaincodeStub which the chaincode can use to callback
stub := new(ChaincodeStub)
stub.init(handler, msg.Txid, input, msg.ProposalContext)
err := stub.init(handler, msg.Txid, input, msg.Proposal)
if err != nil {
payload := []byte(err.Error())
// Send ERROR message to chaincode support and change state
chaincodeLogger.Errorf("[%s]Transaction execution failed. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR)
nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent}
return
}
res := handler.cc.Invoke(stub)

// Endorser will handle error contained in Response.
Expand Down
18 changes: 11 additions & 7 deletions core/chaincode/shim/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,22 @@ type ChaincodeStubInterface interface {
// key values across time. GetHistoryForKey is intended to be used for read-only queries.
GetHistoryForKey(key string) (StateQueryIteratorInterface, error)

// GetCallerCertificate returns caller certificate
GetCallerCertificate() ([]byte, error)
// GetCreator returns SignatureHeader.Creator of the proposal
// this Stub refers to.
GetCreator() ([]byte, error)

// GetCallerMetadata returns caller metadata
GetCallerMetadata() ([]byte, error)
// GetTransient returns the ChaincodeProposalPayload.transient field.
// It is a map that contains data (e.g. cryptographic material)
// that might be used to implement some form of application-level confidentiality. The contents
// of this field, as prescribed by ChaincodeProposalPayload, are supposed to always
// be omitted from the transaction and excluded from the ledger.
GetTransient() (map[string][]byte, error)

// GetBinding returns the transaction binding
GetBinding() ([]byte, error)

// GetPayload returns transaction payload, which is a `ChaincodeSpec` defined
// in fabric/protos/chaincode.proto
GetPayload() ([]byte, error)
// GetArgsSlice returns the arguments to the stub call as a byte array
GetArgsSlice() ([]byte, error)

// GetTxTimestamp returns transaction created timestamp, which is currently
// taken from the peer receiving the transaction. Note that this timestamp
Expand Down
3 changes: 3 additions & 0 deletions core/chaincode/shim/java/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ task copyProtos(type:Copy){
from ("${rootDir}/protos/peer"){
include '**/chaincodeevent.proto'
include '**/chaincode.proto'
include '**/chaincodeshim.proto'
include '**/proposal.proto'
include '**/proposal_response.proto'
}
into "${projectDir}/src/main/proto/peer"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperledger.protos.Chaincode.ChaincodeID;
import org.hyperledger.protos.Chaincode.ChaincodeMessage;
import org.hyperledger.protos.Chaincode.ChaincodeMessage.Type;
import org.hyperledger.protos.Chaincodeshim.ChaincodeMessage;
import org.hyperledger.protos.Chaincodeshim.ChaincodeMessage.Type;
import org.hyperledger.protos.ChaincodeSupportGrpc;
import org.hyperledger.protos.ChaincodeSupportGrpc.ChaincodeSupportStub;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperledger.protos.Chaincode;
import org.hyperledger.protos.Chaincodeshim;

import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -102,7 +102,7 @@ public Map<String, String> getStateByRange(String startKey, String endKey) {
*/
public Map<String, ByteString> getStateByRangeRaw(String startKey, String endKey) {
Map<String, ByteString> map = new HashMap<>();
for (Chaincode.QueryStateKeyValue mapping : handler.handleGetStateByRange(
for (Chaincodeshim.QueryStateKeyValue mapping : handler.handleGetStateByRange(
startKey, endKey, uuid).getKeysAndValuesList()) {
map.put(mapping.getKey(), mapping.getValue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
import org.hyperledger.java.fsm.exceptions.NoTransitionException;
import org.hyperledger.java.helper.Channel;
import org.hyperledger.protos.Chaincode.*;
import org.hyperledger.protos.Chaincode.ChaincodeMessage.Builder;
import org.hyperledger.protos.Chaincodeshim.*;
import org.hyperledger.protos.Chaincodeshim.ChaincodeMessage.Builder;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.hyperledger.java.fsm.CallbackType.*;
import static org.hyperledger.protos.Chaincode.ChaincodeMessage.Type.*;
import static org.hyperledger.protos.Chaincodeshim.ChaincodeMessage.Type.*;

public class Handler {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package org.hyperledger.java.shim;

import org.hyperledger.protos.Chaincode.ChaincodeMessage;
import org.hyperledger.protos.Chaincodeshim.ChaincodeMessage;

public class NextStateInfo {

Expand Down
6 changes: 3 additions & 3 deletions core/chaincode/shim/mockstub.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,12 @@ func (stub *MockStub) InvokeChaincode(chaincodeName string, args [][]byte, chann
}

// Not implemented
func (stub *MockStub) GetCallerCertificate() ([]byte, error) {
func (stub *MockStub) GetCreator() ([]byte, error) {
return nil, nil
}

// Not implemented
func (stub *MockStub) GetCallerMetadata() ([]byte, error) {
func (stub *MockStub) GetTransient() (map[string][]byte, error) {
return nil, nil
}

Expand All @@ -266,7 +266,7 @@ func (stub *MockStub) GetBinding() ([]byte, error) {
}

// Not implemented
func (stub *MockStub) GetPayload() ([]byte, error) {
func (stub *MockStub) GetArgsSlice() ([]byte, error) {
return nil, nil
}

Expand Down
Loading

0 comments on commit 458328b

Please sign in to comment.