Skip to content

Commit ec50ad1

Browse files
author
Srinivasan Muralidharan
committed
skeleton Endorser implemention with a CLI driver
This patch is for https://jira.hyperledger.org/browse/FAB-181, FAB-182. The patch is around endorser.go which implements the basic Endorser service. The "peer deploy ..." and "peer invoke .." CLI driver commands have been modified to redirected to use endorser.ProcessProposal instead of the original "devops" calls. The deploy, invoke (and query) CLI calls are unchanged in non-dev mode from user point of view - but for one difference. The response is a ProposalResponse. In dev mode (ie, when peer is started with --peer-chaincodedev) the deploy command would need to set "CORE_CHAINCODE_MODE=dev" in the "peer chaincode deploy ..." command. This is because, the command now computes the chaincode code just like the SDK as opposed to leaving it to be done by devops in the peer. Example CORE_CHAINCODE_MODE=dev CORE_LOGGING_LEVEL=debug ./peer chaincode deploy -n mycc -c '{"Args":["init","a","100","b","200"]}' Change-Id: Ie6e44cef880bfcbeb7619f135566a7dce9dcdbc2 Signed-off-by: Srinivasan Muralidharan <[email protected]>
1 parent 9ec4873 commit ec50ad1

File tree

13 files changed

+1180
-59
lines changed

13 files changed

+1180
-59
lines changed

core/chaincode/chaincode_support.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -597,9 +597,9 @@ func (chaincodeSupport *ChaincodeSupport) Deploy(context context.Context, t *pb.
597597
chaincodeLogger.Debugf("deploying chaincode %s(networkid:%s,peerid:%s)", chaincode, chaincodeSupport.peerNetworkID, chaincodeSupport.peerID)
598598

599599
//create image and create container
600-
_, err = container.VMCProcess(context, vmtype, cir)
601-
if err != nil {
602-
err = fmt.Errorf("Error starting container: %s", err)
600+
resp, err2 := container.VMCProcess(context, vmtype, cir)
601+
if err2 != nil || (resp != nil && resp.(container.VMCResp).Err != nil) {
602+
err = fmt.Errorf("Error creating image: %s", err2)
603603
}
604604

605605
return cds, err

core/chaincode/chaincodeexec.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package chaincode
18+
19+
import (
20+
"golang.org/x/net/context"
21+
22+
"fmt"
23+
24+
"github.com/hyperledger/fabric/core/ledger"
25+
"github.com/hyperledger/fabric/core/util"
26+
pb "github.com/hyperledger/fabric/protos"
27+
)
28+
29+
//create a Transactions - this has to change to Proposal when we move chaincode to use Proposals
30+
func createTx(typ pb.Transaction_Type, ccname string, args [][]byte) (*pb.Transaction, error) {
31+
var tx *pb.Transaction
32+
var err error
33+
uuid := util.GenerateUUID()
34+
spec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: ccname}, CtorMsg: &pb.ChaincodeInput{Args: args}}}
35+
tx, err = pb.NewChaincodeExecute(spec, uuid, typ)
36+
if nil != err {
37+
return nil, err
38+
}
39+
return tx, nil
40+
}
41+
42+
// ExecuteChaincode executes a given chaincode given chaincode name and arguments
43+
func ExecuteChaincode(typ pb.Transaction_Type, chainname string, ccname string, args [][]byte) ([]byte, error) {
44+
var tx *pb.Transaction
45+
var err error
46+
var b []byte
47+
var lgr *ledger.Ledger
48+
tx, err = createTx(typ, ccname, args)
49+
lgr, err = ledger.GetLedger()
50+
if err != nil {
51+
return nil, fmt.Errorf("Failed to get handle to ledger: %s ", err)
52+
}
53+
//TODO - new ledger access will change this call to take a context
54+
lgr.BeginTxBatch("1")
55+
b, _, err = Execute(context.Background(), GetChain(ChainName(chainname)), tx)
56+
if err != nil {
57+
return nil, fmt.Errorf("Error deploying chaincode: %s", err)
58+
}
59+
//TODO - new ledger access will change this call to take a context
60+
lgr.CommitTxBatch("1", []*pb.Transaction{tx}, nil, nil)
61+
62+
return b, err
63+
}

core/db/db_test_exports.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ func (testDB *TestDBWrapper) CleanDB(t testing.TB) {
4444
// at the end of the test
4545
testDB.cleanup()
4646
testDB.removeDBPath()
47-
t.Logf("Creating testDB")
47+
48+
//if we are cleanup once in Main, we don't have a t
49+
if t != nil {
50+
t.Logf("Creating testDB")
51+
}
4852

4953
Start()
5054
testDB.performCleanup = true

core/devops.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ func (*Devops) Build(context context.Context, spec *pb.ChaincodeSpec) (*pb.Chain
122122
return chaincodeDeploymentSpec, nil
123123
}
124124

125-
// get chaincode bytes
126-
func (*Devops) getChaincodeBytes(context context.Context, spec *pb.ChaincodeSpec) (*pb.ChaincodeDeploymentSpec, error) {
125+
// GetChaincodeBytes get chaincode deployment spec given the chaincode spec
126+
func GetChaincodeBytes(context context.Context, spec *pb.ChaincodeSpec) (*pb.ChaincodeDeploymentSpec, error) {
127127
mode := viper.GetString("chaincode.mode")
128128
var codePackageBytes []byte
129129
if mode != chaincode.DevModeUserRunsChaincode {
@@ -147,7 +147,7 @@ func (*Devops) getChaincodeBytes(context context.Context, spec *pb.ChaincodeSpec
147147
// Deploy deploys the supplied chaincode image to the validators through a transaction
148148
func (d *Devops) Deploy(ctx context.Context, spec *pb.ChaincodeSpec) (*pb.ChaincodeDeploymentSpec, error) {
149149
// get the deployment spec
150-
chaincodeDeploymentSpec, err := d.getChaincodeBytes(ctx, spec)
150+
chaincodeDeploymentSpec, err := GetChaincodeBytes(ctx, spec)
151151

152152
if err != nil {
153153
devopsLogger.Error(fmt.Sprintf("Error deploying chaincode spec: %v\n\n error: %s", spec, err))

core/endorser/config.go

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package endorser
18+
19+
import (
20+
"flag"
21+
"fmt"
22+
"strings"
23+
24+
"github.com/op/go-logging"
25+
"github.com/spf13/viper"
26+
)
27+
28+
// Config the config wrapper structure
29+
type Config struct {
30+
}
31+
32+
func init() {
33+
34+
}
35+
36+
// SetupTestLogging setup the logging during test execution
37+
func SetupTestLogging() {
38+
level, err := logging.LogLevel(viper.GetString("logging.peer"))
39+
if err == nil {
40+
// No error, use the setting
41+
logging.SetLevel(level, "main")
42+
logging.SetLevel(level, "server")
43+
logging.SetLevel(level, "peer")
44+
} else {
45+
logging.SetLevel(logging.ERROR, "main")
46+
logging.SetLevel(logging.ERROR, "server")
47+
logging.SetLevel(logging.ERROR, "peer")
48+
}
49+
}
50+
51+
// SetupTestConfig setup the config during test execution
52+
func SetupTestConfig() {
53+
flag.Parse()
54+
55+
// Now set the configuration file
56+
viper.SetEnvPrefix("CORE")
57+
viper.AutomaticEnv()
58+
replacer := strings.NewReplacer(".", "_")
59+
viper.SetEnvKeyReplacer(replacer)
60+
viper.SetConfigName("endorser") // name of config file (without extension)
61+
viper.AddConfigPath("./") // path to look for the config file in
62+
err := viper.ReadInConfig() // Find and read the config file
63+
if err != nil { // Handle errors reading the config file
64+
panic(fmt.Errorf("Fatal error config file: %s \n", err))
65+
}
66+
67+
SetupTestLogging()
68+
69+
// Set the number of maxprocs
70+
viper.GetInt("peer.gomaxprocs")
71+
}

core/endorser/endorser.go

+103-13
Original file line numberDiff line numberDiff line change
@@ -21,45 +21,135 @@ import (
2121
"github.com/op/go-logging"
2222
"golang.org/x/net/context"
2323

24+
"github.com/hyperledger/fabric/core/chaincode"
2425
"github.com/hyperledger/fabric/core/peer"
2526
"github.com/hyperledger/fabric/core/util"
2627
pb "github.com/hyperledger/fabric/protos"
2728
)
2829

2930
var devopsLogger = logging.MustGetLogger("devops")
3031

32+
// The Jira issue that documents Endorser flow along with its relationship to
33+
// the lifecycle chaincode - https://jira.hyperledger.org/browse/FAB-181
34+
35+
// Endorser provides the Endorser service ProcessProposal
3136
type Endorser struct {
3237
coord peer.MessageHandlerCoordinator
3338
}
3439

35-
// NewDevopsServer creates and returns a new Devops server instance.
40+
// NewEndorserServer creates and returns a new Endorser server instance.
3641
func NewEndorserServer(coord peer.MessageHandlerCoordinator) pb.EndorserServer {
3742
e := new(Endorser)
3843
e.coord = coord
3944
return e
4045
}
4146

42-
// ProcessProposal process the Proposal
43-
func (e *Endorser) ProcessProposal(ctx context.Context, proposal *pb.Proposal) (*pb.ProposalResponse, error) {
44-
// if err := crypto.RegisterClient(secret.EnrollId, nil, secret.EnrollId, secret.EnrollSecret); nil != err {
45-
// return &pb.Response{Status: pb.Response_FAILURE, Msg: []byte(err.Error())}, nil
46-
// }
47+
//get the ChaincodeInvocationSpec from the proposal
48+
func (*Endorser) getChaincodeInvocationSpec(prop *pb.Proposal) (*pb.ChaincodeInvocationSpec, error) {
49+
cis := &pb.ChaincodeInvocationSpec{}
50+
err := proto.Unmarshal(prop.Payload, cis)
51+
if err != nil {
52+
return nil, err
53+
}
54+
return cis, nil
55+
}
4756

48-
// Create a dummy action
49-
action := &pb.Action{ProposalHash: util.ComputeCryptoHash(proposal.Payload), SimulationResult: []byte("TODO: Simulated Result")}
57+
//TODO - what would Endorser's ACL be ?
58+
func (*Endorser) checkACL(prop *pb.Proposal) error {
59+
return nil
60+
}
5061

51-
actionBytes, err := proto.Marshal(action)
62+
//TODO - check for escc and vscc
63+
func (*Endorser) checkEsccAndVscc(prop *pb.Proposal) error {
64+
return nil
65+
}
66+
67+
//call specified chaincode (system or user)
68+
func (*Endorser) callChaincode(cis *pb.ChaincodeInvocationSpec) ([]byte, error) {
69+
//TODO - get chainname from cis when defined
70+
chainName := string(chaincode.DefaultChain)
71+
b, err := chaincode.ExecuteChaincode(pb.Transaction_CHAINCODE_INVOKE, chainName, cis.ChaincodeSpec.ChaincodeID.Name, cis.ChaincodeSpec.CtorMsg.Args)
72+
return b, err
73+
}
74+
75+
//simulate the proposal by calling the chaincode
76+
func (e *Endorser) simulateProposal(prop *pb.Proposal) ([]byte, []byte, error) {
77+
//we do expect the payload to be a ChaincodeInvocationSpec
78+
//if we are supporting other payloads in future, this be glaringly point
79+
//as something that should change
80+
cis, err := e.getChaincodeInvocationSpec(prop)
5281
if err != nil {
53-
return nil, err
82+
return nil, nil, err
83+
}
84+
//---1. check ACL
85+
if err = e.checkACL(prop); err != nil {
86+
return nil, nil, err
87+
}
88+
89+
//---2. check ESCC and VSCC for the chaincode
90+
if err = e.checkEsccAndVscc(prop); err != nil {
91+
return nil, nil, err
5492
}
5593

56-
sig, err := e.coord.GetSecHelper().Sign(actionBytes)
94+
//---3. execute the proposal
95+
var resp []byte
96+
resp, err = e.callChaincode(cis)
5797
if err != nil {
58-
return nil, err
98+
return nil, nil, err
5999
}
60100

61-
endorsement := &pb.Endorsement{Signature: sig}
101+
//---4. get simulation results
102+
103+
simulationResult := []byte("TODO: sim results")
104+
return resp, simulationResult, nil
105+
}
106+
107+
//endorse the proposal by calling the ESCC
108+
func (e *Endorser) endorseProposal(proposal *pb.Proposal) (*pb.Endorsement, error) {
109+
/************ TODO
110+
//---4. call ESCC
111+
args := util.ToChaincodeArgs("", "serialized_action", "serialized_proposal", "any", "other", "args")
112+
ecccis := &pb.ChaincodeInvocationSpec{ ChaincodeSpec: &pb.ChaincodeSpec{ Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{ Name: "escc" }, CtorMsg: &pb.ChaincodeInput{ Args: args }}}
62113
114+
var sig []byte
115+
sig, err = e.callChaincode(ecccis)
116+
if err != nil {
117+
return err
118+
}
119+
************/
120+
121+
endorsement := &pb.Endorsement{Signature: []byte("TODO Signature")}
122+
return endorsement, nil
123+
}
124+
125+
// ProcessProposal process the Proposal
126+
func (e *Endorser) ProcessProposal(ctx context.Context, prop *pb.Proposal) (*pb.ProposalResponse, error) {
127+
//1 -- simulate
128+
//TODO what do we do with response ? We need it for Invoke responses for sure
129+
//Which field in PayloadResponse will carry return value ?
130+
_, simulationResult, err := e.simulateProposal(prop)
131+
if err != nil {
132+
return &pb.ProposalResponse{Response: &pb.Response2{Status: 500, Message: err.Error()}}, err
133+
}
134+
135+
//2 -- endorse
136+
//TODO what do we do with response ? We need it for Invoke responses for sure
137+
endorsement, err := e.endorseProposal(prop)
138+
if err != nil {
139+
return &pb.ProposalResponse{Response: &pb.Response2{Status: 500, Message: err.Error()}}, err
140+
}
141+
142+
//3 -- respond
143+
// Create action
144+
action := &pb.Action{ProposalHash: util.ComputeCryptoHash(prop.Payload), SimulationResult: simulationResult}
145+
146+
actionBytes, err := proto.Marshal(action)
147+
if err != nil {
148+
return nil, err
149+
}
150+
151+
//TODO when we have additional field in response, use "resp" bytes from the simulation
63152
resp := &pb.Response2{Status: 200, Message: "Proposal accepted"}
153+
64154
return &pb.ProposalResponse{Response: resp, ActionBytes: actionBytes, Endorsement: endorsement}, nil
65155
}

0 commit comments

Comments
 (0)