Skip to content

Commit

Permalink
Invoke VSCC from committer
Browse files Browse the repository at this point in the history
This change-set adds the call to VSCC to the committer's code path. There are
still a few missing pieces that we can only address once LCCC is finalized
(e.g. identify the right VSCC, extract policies). The change-set also contains
a minor refactoring of the tx-assembly code as discussed with Manish.

Change-Id: Ibf8807dbb2934dcf4929c3e1a3edd4e5ec9f40ea
Signed-off-by: Alessandro Sorniotti <ale.linux@sopit.net>
  • Loading branch information
ale-linux committed Dec 2, 2016
1 parent de00eaa commit 7b8dbdf
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 72 deletions.
55 changes: 54 additions & 1 deletion core/committer/noopssinglechain/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ import (

"fmt"

"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/gossip/gossip"
"github.com/hyperledger/fabric/gossip/integration"
gossip_proto "github.com/hyperledger/fabric/gossip/proto"
"github.com/hyperledger/fabric/gossip/state"
pb "github.com/hyperledger/fabric/protos/peer"
)

var logger *logging.Logger // package-level logger
Expand Down Expand Up @@ -224,6 +226,49 @@ func (d *DeliverService) isDone() bool {
return atomic.LoadInt32(&d.stopFlag) == 1
}

func isTxValidForVscc(envBytes []byte) error {
// TODO: Extract the VSCC/policy from LCCC as soon as this is ready
vscc := "vscc"

// TODO - get chainname from the envelope
chainName := string(chaincode.DefaultChain)

txid := "N/A" // FIXME: is that appropriate?

// build arguments for VSCC invocation
// args[0] - function name (not used now)
// args[1] - serialized Envelope
args := [][]byte{[]byte(""), envBytes}

// create VSCC invocation proposal
vsccCis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Name: vscc}, CtorMsg: &pb.ChaincodeInput{Args: args}}}
prop, err := putils.CreateProposalFromCIS(txid, util.GetTestChainID(), vsccCis, []byte(""))
if err != nil {
logger.Errorf("Cannot create a proposal to invoke VSCC, err %s\n", err)
return err
}

// get context for the chaincode execution
var txsim ledger.TxSimulator
lgr := kvledger.GetLedger(chainName)
txsim, err = lgr.NewTxSimulator()
if err != nil {
logger.Errorf("Cannot obtain tx simulator, err %s\n", err)
return err
}
defer txsim.Done()
ctxt := context.WithValue(context.Background(), chaincode.TXSimulatorKey, txsim)

// invoke VSCC
_, _, err = chaincode.ExecuteChaincode(ctxt, txid, prop, chainName, vscc, args)
if err != nil {
logger.Errorf("VSCC check failed for transaction, error %s", err)
return err
}

return nil
}

func (d *DeliverService) readUntilClose() {
for {
msg, err := d.client.Recv()
Expand Down Expand Up @@ -264,7 +309,15 @@ func (d *DeliverService) readUntilClose() {
// in validation is just dropped on the floor
logger.Errorf("Invalid transaction, error %s", err)
} else {
// TODO: call VSCC now
err = isTxValidForVscc(d)
if err != nil {
// TODO: this code needs to receive a bit more attention and discussion:
// it's not clear what it means if a transaction which causes a failure
// in validation is just dropped on the floor
logger.Errorf("isTxValidForVscc returned error %s", err)
continue
}

if t, err := proto.Marshal(env); err == nil {
block.Data.Data = append(block.Data.Data, t)
} else {
Expand Down
15 changes: 15 additions & 0 deletions core/system_chaincode/vscc/validator_onevalidsignature.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ import (
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"
)

var logger = logging.MustGetLogger("vscc")

// ValidatorOneValidSignature implements the default transaction validation policy,
// which is to check the correctness of the read-write set and the endorsement
// signatures
Expand Down Expand Up @@ -59,33 +62,40 @@ func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface)
return nil, errors.New("No block to validate")
}

logger.Infof("VSCC invoked")

// get the envelope...
env, err := utils.GetEnvelope(args[1])
if err != nil {
logger.Errorf("VSCC error: GetEnvelope failed, err %s", err)
return nil, err
}

// ...and the payload...
payl, err := utils.GetPayload(env)
if err != nil {
logger.Errorf("VSCC error: GetPayload failed, err %s", err)
return nil, err
}

// validate the payload type
if common.HeaderType(payl.Header.ChainHeader.Type) != common.HeaderType_ENDORSER_TRANSACTION {
logger.Errorf("Only Endorser Transactions are supported, provided type %d", payl.Header.ChainHeader.Type)
return nil, fmt.Errorf("Only Endorser Transactions are supported, provided type %d", payl.Header.ChainHeader.Type)
}

// ...and the transaction...
tx, err := utils.GetTransaction(payl.Data)
if err != nil {
logger.Errorf("VSCC error: GetTransaction failed, err %s", err)
return nil, err
}

// loop through each of the actions within
for _, act := range tx.Actions {
cap, err := utils.GetChaincodeActionPayload(act.Payload)
if err != nil {
logger.Errorf("VSCC error: GetChaincodeActionPayload failed, err %s", err)
return nil, err
}

Expand All @@ -97,23 +107,28 @@ func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface)
// extract the identity of the signer
end, err := msp.GetManager().DeserializeIdentity(endorsement.Endorser)
if err != nil {
logger.Errorf("VSCC error: DeserializeIdentity failed, err %s", err)
return nil, err
}

// validate it
valid, err := end.Validate()
if err != nil || !valid {
logger.Errorf("Invalid endorser, err %s, valid %t", err, valid)
return nil, fmt.Errorf("Invalid endorser, err %s, valid %t", err, valid)
}

// verify the signature
valid, err = end.Verify(append(prespBytes, endorsement.Endorser...), endorsement.Signature)
if err != nil || !valid {
logger.Errorf("Invalid signature, err %s, valid %t", err, valid)
return nil, fmt.Errorf("Invalid signature, err %s, valid %t", err, valid)
}
}
}

logger.Infof("VSCC exists successfully")

return nil, nil
}

Expand Down
21 changes: 7 additions & 14 deletions protos/testutils/txtestutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,15 @@ func ConstructSingedTxEnv(txid string, chainID string, ccName string, simulation
return env, nil
}

var mspLcl msp.PeerMSP
var sigId msp.SigningIdentity

// ConstructUnsingedTxEnv creates a Transaction envelope from given inputs
func ConstructUnsingedTxEnv(txid string, chainID string, ccName string, simulationResults []byte, events []byte, visibility []byte) (*common.Envelope, error) {
prop, err := putils.CreateChaincodeProposal(txid, chainID, &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeID: &pb.ChaincodeID{Name: ccName}}}, nil)
if err != nil {
return nil, err
}

presp, err := putils.ConstructUnsignedProposalResponse(prop.Header, prop.Payload, simulationResults, nil, nil)
if err != nil {
return nil, err
}

env, err := putils.ConstructUnsignedTxEnvelope(prop, presp)
if err != nil {
return nil, err
if mspLcl == nil {
mspLcl = msp.NewNoopMsp()
sigId, _ = mspLcl.GetSigningIdentity(nil)
}
return env, nil

return ConstructSingedTxEnv(txid, chainID, ccName, simulationResults, events, visibility, sigId)
}
91 changes: 34 additions & 57 deletions protos/utils/txutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func GetPayloads(txActions *peer.TransactionAction) (*peer.ChaincodeActionPayloa
return ccPayload, respPayload, nil
}

// GetEndorserTxFromBlock gets Transaction2 from Block.Data.Data
// GetEndorserTxFromBlock gets Transaction from Block.Data.Data
func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) {
//Block always begins with an envelope
var err error
Expand All @@ -71,42 +71,10 @@ func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) {
return env, nil
}

// CreateSignedTx assembles an Envelope message from proposal, endorsements and a signer.
// assemble an Envelope message from proposal, endorsements and a signer.
// This function should be called by a client when it has collected enough endorsements
// for a proposal to create a transaction and submit it to peers for ordering
func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
// the original header
hdr, err := GetHeader(proposal.Header)
if err != nil {
return nil, fmt.Errorf("Could not unmarshal the proposal header")
}
// check that the signer is the same that is referenced in the header
// TODO: maybe worth removing?
signerBytes, err := signer.Serialize()
if err != nil {
return nil, err
}

if bytes.Compare(signerBytes, hdr.SignatureHeader.Creator) != 0 {
return nil, fmt.Errorf("The signer needs to be the same as the one referenced in the header")
}

// create the payload
txEnvelope, err := ConstructUnsignedTxEnvelope(proposal, resps...)
if err != nil {
return nil, err
}
// sign the payload
sig, err := signer.Sign(txEnvelope.Payload)
if err != nil {
return nil, err
}
txEnvelope.Signature = sig
return txEnvelope, nil
}

// ConstructUnsignedTxEnvelope constructs payload for the transaction from proposal and endorsements.
func ConstructUnsignedTxEnvelope(proposal *peer.Proposal, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
if len(resps) == 0 {
return nil, fmt.Errorf("At least one proposal response is necessary")
}
Expand All @@ -123,6 +91,17 @@ func ConstructUnsignedTxEnvelope(proposal *peer.Proposal, resps ...*peer.Proposa
return nil, fmt.Errorf("Could not unmarshal the proposal payload")
}

// check that the signer is the same that is referenced in the header
// TODO: maybe worth removing?
signerBytes, err := signer.Serialize()
if err != nil {
return nil, err
}

if bytes.Compare(signerBytes, hdr.SignatureHeader.Creator) != 0 {
return nil, fmt.Errorf("The signer needs to be the same as the one referenced in the header")
}

// get header extensions so we have the visibility field
hdrExt, err := GetChaincodeHeaderExtension(hdr)
if err != nil {
Expand Down Expand Up @@ -184,40 +163,25 @@ func ConstructUnsignedTxEnvelope(proposal *peer.Proposal, resps ...*peer.Proposa
if err != nil {
return nil, err
}

// create the payload
payl := &common.Payload{Header: hdr, Data: txBytes}
paylBytes, err := GetBytesPayload(payl)
if err != nil {
return nil, err
}

// here's the envelope
return &common.Envelope{Payload: paylBytes, Signature: nil}, nil
}

// CreateProposalResponse creates the proposal response and endorses the payload
func CreateProposalResponse(hdr []byte, payl []byte, results []byte, events []byte, visibility []byte, signingEndorser msp.SigningIdentity) (*peer.ProposalResponse, error) {
resp, err := ConstructUnsignedProposalResponse(hdr, payl, results, events, visibility)
// sign the payload
sig, err := signer.Sign(paylBytes)
if err != nil {
return nil, err
}
// serialize the signing identity
endorser, err := signingEndorser.Serialize()
if err != nil {
return nil, fmt.Errorf("Could not serialize the signing identity for %s, err %s", signingEndorser.Identifier(), err)
}

// sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key
signature, err := signingEndorser.Sign(append(resp.Payload, endorser...))
if err != nil {
return nil, fmt.Errorf("Could not sign the proposal response payload, err %s", err)
}
resp.Endorsement.Endorser = endorser
resp.Endorsement.Signature = signature
return resp, nil
// here's the envelope
return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
}

// ConstructUnsignedProposalResponse constructs the proposal response structure only
func ConstructUnsignedProposalResponse(hdr []byte, payl []byte, results []byte, events []byte, visibility []byte) (*peer.ProposalResponse, error) {
func CreateProposalResponse(hdr []byte, payl []byte, results []byte, events []byte, visibility []byte, signingEndorser msp.SigningIdentity) (*peer.ProposalResponse, error) {
// obtain the proposal hash given proposal header, payload and the requested visibility
pHashBytes, err := GetProposalHash1(hdr, payl, visibility)
if err != nil {
Expand All @@ -229,10 +193,23 @@ func ConstructUnsignedProposalResponse(hdr []byte, payl []byte, results []byte,
if err != nil {
return nil, errors.New("Failure while unmarshalling the ProposalResponsePayload")
}

// serialize the signing identity
endorser, err := signingEndorser.Serialize()
if err != nil {
return nil, fmt.Errorf("Could not serialize the signing identity for %s, err %s", signingEndorser.Identifier(), err)
}

// sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key
signature, err := signingEndorser.Sign(append(prpBytes, endorser...))
if err != nil {
return nil, fmt.Errorf("Could not sign the proposal response payload, err %s", err)
}

resp := &peer.ProposalResponse{
// Timestamp: TODO!
Version: 1, // TODO: pick right version number
Endorsement: &peer.Endorsement{Signature: nil, Endorser: nil},
Endorsement: &peer.Endorsement{Signature: signature, Endorser: endorser},
Payload: prpBytes,
Response: &peer.Response{Status: 200, Message: "OK"}}

Expand Down

0 comments on commit 7b8dbdf

Please sign in to comment.