Skip to content

Commit d632e74

Browse files
author
Srinivasan Muralidharan
committed
FAB-437 bare-minimum, end to end skeleton using solo
The main purpose of this checking . show commit followed by simulation in action . users can get a feel for the flow of the protocol across the different legs of the end-end path . identify key areas for attention (esp. grep for "!!IMPORTANT!!") "deploy and "invoke" Chaincode commands from CLI will also convert a successful proposal response into a transaction and send it to the Orderer (if configured in core.yaml). "query" is removed from CLI. Invoke can also return values now in ProposalResponse.Response.Payload. REST calls should not be affected and should work with old ledger. See core.yaml for default orderer setup. This also introduces a stop-gap "committer" whose only task is to commit blocks from the orderer. To test : 1. Terminal 1 - run the "solo" orderer cd fabric/orderer go build ./orderer 2. Terminal 2 - run the peer peer node start 1>/tmp/peer.out 2>&1 3. Terminal 3 - deploy and invoke take usual params peer chaincode deploy ... 1>/tmp/out 2>&1 peer chaincode invoke ... 1>/tmp/out 2>&1 /tmp/peer.out and /tmp/out will contain tell tale signs of the round trip in action. Change-Id: Ic1aa31993fc57ce145c39967d4d682fd2dc5704b Signed-off-by: Srinivasan Muralidharan <[email protected]>
1 parent 423334f commit d632e74

File tree

14 files changed

+481
-32
lines changed

14 files changed

+481
-32
lines changed

core/chaincode/chaincode_support.go

+29-14
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/hyperledger/fabric/core/container/ccintf"
3535
"github.com/hyperledger/fabric/core/crypto"
3636
"github.com/hyperledger/fabric/core/ledger"
37+
ledgernext "github.com/hyperledger/fabric/core/ledgernext"
3738
pb "github.com/hyperledger/fabric/protos"
3839
)
3940

@@ -490,23 +491,37 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.
490491
}
491492

492493
//hopefully we are restarting from existing image and the deployed transaction exists
493-
depTx, ledgerErr = ledger.GetTransactionByID(chaincode)
494-
if ledgerErr != nil {
495-
return cID, cMsg, fmt.Errorf("Could not get deployment transaction for %s - %s", chaincode, ledgerErr)
496-
}
497-
if depTx == nil {
498-
return cID, cMsg, fmt.Errorf("deployment transaction does not exist for %s", chaincode)
499-
}
500-
if nil != chaincodeSupport.secHelper {
501-
var err error
502-
depTx, err = chaincodeSupport.secHelper.TransactionPreExecution(depTx)
503-
// Note that t is now decrypted and is a deep clone of the original input t
504-
if nil != err {
505-
return cID, cMsg, fmt.Errorf("failed tx preexecution%s - %s", chaincode, err)
494+
var depPayload []byte
495+
if _, ok := context.Value(TXSimulatorKey).(ledgernext.TxSimulator); ok {
496+
depPayload, ledgerErr = getCDSFromLCCC(context, string(DefaultChain), chaincode)
497+
if ledgerErr != nil {
498+
return cID, cMsg, fmt.Errorf("Could not get deployment transaction from LCCC for %s - %s", chaincode, ledgerErr)
499+
}
500+
} else {
501+
depTx, ledgerErr = ledger.GetTransactionByID(chaincode)
502+
if ledgerErr != nil {
503+
return cID, cMsg, fmt.Errorf("Could not get deployment transaction for %s - %s", chaincode, ledgerErr)
504+
}
505+
if depTx == nil {
506+
return cID, cMsg, fmt.Errorf("deployment transaction does not exist for %s", chaincode)
506507
}
508+
if nil != chaincodeSupport.secHelper {
509+
var err error
510+
depTx, err = chaincodeSupport.secHelper.TransactionPreExecution(depTx)
511+
// Note that t is now decrypted and is a deep clone of the original input t
512+
if nil != err {
513+
return cID, cMsg, fmt.Errorf("failed tx preexecution%s - %s", chaincode, err)
514+
}
515+
}
516+
depPayload = depTx.Payload
517+
}
518+
519+
if depPayload == nil {
520+
return cID, cMsg, fmt.Errorf("failed to get deployment payload %s - %s", chaincode, ledgerErr)
507521
}
522+
508523
//Get lang from original deployment
509-
err := proto.Unmarshal(depTx.Payload, cds)
524+
err = proto.Unmarshal(depPayload, cds)
510525
if err != nil {
511526
return cID, cMsg, fmt.Errorf("failed to unmarshal deployment transactions for %s - %s", chaincode, err)
512527
}

core/chaincode/chaincodeexec.go

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ func createTx(typ pb.Transaction_Type, ccname string, args [][]byte) (*pb.Transa
3838
return tx, nil
3939
}
4040

41+
func getCDSFromLCCC(ctxt context.Context, chainID string, chaincodeID string) ([]byte, error) {
42+
return ExecuteChaincode(ctxt, pb.Transaction_CHAINCODE_INVOKE, string(DefaultChain), "lccc", [][]byte{[]byte("getdepspec"), []byte(chainID), []byte(chaincodeID)})
43+
}
44+
4145
// ExecuteChaincode executes a given chaincode given chaincode name and arguments
4246
func ExecuteChaincode(ctxt context.Context, typ pb.Transaction_Type, chainname string, ccname string, args [][]byte) ([]byte, error) {
4347
var tx *pb.Transaction

core/committer/committer.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 committer
18+
19+
// Committer is the interface supported by committers
20+
// The only committer is noopssinglechain committer.
21+
// The interface is intentionally sparse with the sole
22+
// aim of "leave-everything-to-the-committer-for-now".
23+
// As we solidify the bootstrap process and as we add
24+
// more support (such as Gossip) this interface will
25+
// change
26+
type Committer interface {
27+
//Start registers and opens communications
28+
Start() error
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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 noopssinglechain
18+
19+
import (
20+
"fmt"
21+
"time"
22+
23+
"github.com/op/go-logging"
24+
"github.com/spf13/viper"
25+
26+
"github.com/golang/protobuf/proto"
27+
"github.com/hyperledger/fabric/core/chaincode"
28+
"github.com/hyperledger/fabric/core/committer"
29+
"github.com/hyperledger/fabric/core/ledgernext/kvledger"
30+
ab "github.com/hyperledger/fabric/orderer/atomicbroadcast"
31+
"golang.org/x/net/context"
32+
"google.golang.org/grpc"
33+
34+
pb "github.com/hyperledger/fabric/protos"
35+
)
36+
37+
//--------!!!IMPORTANT!!-!!IMPORTANT!!-!!IMPORTANT!!---------
38+
// This Orderer is based off fabric/orderer/sample_clients/
39+
// deliver_stdout/client.go. This is used merely to complete
40+
// the loop for the "skeleton" path so we can reason about and
41+
// modify committer component more effectively using code.
42+
43+
var logger *logging.Logger // package-level logger
44+
45+
func init() {
46+
logger = logging.MustGetLogger("noopssinglechain")
47+
}
48+
49+
type deliverClient struct {
50+
client ab.AtomicBroadcast_DeliverClient
51+
windowSize uint64
52+
unAcknowledged uint64
53+
solo *solo
54+
}
55+
56+
func newDeliverClient(client ab.AtomicBroadcast_DeliverClient, windowSize uint64, solo *solo) *deliverClient {
57+
return &deliverClient{client: client, windowSize: windowSize, solo: solo}
58+
}
59+
60+
func (r *deliverClient) seekOldest() error {
61+
return r.client.Send(&ab.DeliverUpdate{
62+
Type: &ab.DeliverUpdate_Seek{
63+
Seek: &ab.SeekInfo{
64+
Start: ab.SeekInfo_OLDEST,
65+
WindowSize: r.windowSize,
66+
},
67+
},
68+
})
69+
}
70+
71+
func (r *deliverClient) seekNewest() error {
72+
return r.client.Send(&ab.DeliverUpdate{
73+
Type: &ab.DeliverUpdate_Seek{
74+
Seek: &ab.SeekInfo{
75+
Start: ab.SeekInfo_NEWEST,
76+
WindowSize: r.windowSize,
77+
},
78+
},
79+
})
80+
}
81+
82+
func (r *deliverClient) seek(blockNumber uint64) error {
83+
return r.client.Send(&ab.DeliverUpdate{
84+
Type: &ab.DeliverUpdate_Seek{
85+
Seek: &ab.SeekInfo{
86+
Start: ab.SeekInfo_SPECIFIED,
87+
SpecifiedNumber: blockNumber,
88+
WindowSize: r.windowSize,
89+
},
90+
},
91+
})
92+
}
93+
94+
// constructBlock constructs a block from a list of transactions
95+
func (r *deliverClient) constructBlock(transactions []*pb.Transaction2) *pb.Block2 {
96+
block := &pb.Block2{}
97+
for _, tx := range transactions {
98+
txBytes, _ := proto.Marshal(tx)
99+
block.Transactions = append(block.Transactions, txBytes)
100+
}
101+
return block
102+
}
103+
104+
// commit the received transaction
105+
func (r *deliverClient) commit(txs []*pb.Transaction2) error {
106+
rawblock := r.constructBlock(txs)
107+
108+
lgr := kvledger.GetLedger(r.solo.ledger)
109+
110+
var err error
111+
if _, _, err = lgr.RemoveInvalidTransactionsAndPrepare(rawblock); err != nil {
112+
return err
113+
}
114+
if err = lgr.Commit(); err != nil {
115+
return err
116+
}
117+
return err
118+
}
119+
120+
func (r *deliverClient) readUntilClose() {
121+
for {
122+
msg, err := r.client.Recv()
123+
if err != nil {
124+
return
125+
}
126+
127+
switch t := msg.Type.(type) {
128+
case *ab.DeliverResponse_Error:
129+
if t.Error == ab.Status_SUCCESS {
130+
fmt.Println("ERROR! Received success in error field")
131+
return
132+
}
133+
fmt.Println("Got error ", t)
134+
case *ab.DeliverResponse_Block:
135+
txs := []*pb.Transaction2{}
136+
for _, d := range t.Block.Messages {
137+
if d != nil && d.Data != nil {
138+
tx := &pb.Transaction2{}
139+
if err = proto.Unmarshal(d.Data, tx); err != nil {
140+
fmt.Printf("Error getting tx(%s)...dropping block\n", err)
141+
continue
142+
}
143+
txs = append(txs, tx)
144+
}
145+
}
146+
if err = r.commit(txs); err != nil {
147+
fmt.Printf("Got error while committing(%s)\n", err)
148+
} else {
149+
fmt.Printf("Commit success, created a block!\n", err)
150+
}
151+
152+
r.unAcknowledged++
153+
if r.unAcknowledged >= r.windowSize/2 {
154+
fmt.Println("Sending acknowledgement")
155+
err = r.client.Send(&ab.DeliverUpdate{Type: &ab.DeliverUpdate_Acknowledgement{Acknowledgement: &ab.Acknowledgement{Number: t.Block.Number}}})
156+
if err != nil {
157+
return
158+
}
159+
r.unAcknowledged = 0
160+
}
161+
default:
162+
fmt.Println("Received unknown: ", t)
163+
return
164+
}
165+
}
166+
}
167+
168+
type solo struct {
169+
//ledger to commit to
170+
ledger string
171+
172+
//orderer to connect to
173+
orderer string
174+
175+
//client of the orderer
176+
client *deliverClient
177+
}
178+
179+
const defaultTimeout = time.Second * 3
180+
181+
//Start establishes communication with an orders
182+
func (s *solo) Start() error {
183+
if s.client != nil {
184+
return fmt.Errorf("Client to (%s) exists", s.orderer)
185+
}
186+
187+
var opts []grpc.DialOption
188+
opts = append(opts, grpc.WithInsecure())
189+
opts = append(opts, grpc.WithTimeout(defaultTimeout))
190+
opts = append(opts, grpc.WithBlock())
191+
conn, err := grpc.Dial(s.orderer, opts...)
192+
if err != nil {
193+
return err
194+
}
195+
var abc ab.AtomicBroadcast_DeliverClient
196+
abc, err = ab.NewAtomicBroadcastClient(conn).Deliver(context.TODO())
197+
if err != nil {
198+
return err
199+
}
200+
201+
s.client = newDeliverClient(abc, 10, s)
202+
if err = s.client.seekOldest(); err != nil {
203+
return err
204+
}
205+
206+
s.client.readUntilClose()
207+
208+
return err
209+
}
210+
211+
// NewCommitter constructs a committer object if not already present
212+
func NewCommitter() committer.Committer {
213+
if viper.GetBool("peer.committer.enabled") {
214+
//TODO ledger needs to be configured, for now just the default
215+
ledger := string(chaincode.DefaultChain)
216+
orderer := viper.GetString("peer.committer.ledger.orderer")
217+
logger.Infof("Creating committer for single noops endorser")
218+
return &solo{ledger: ledger, orderer: orderer}
219+
}
220+
logger.Infof("Committer disabled")
221+
return nil
222+
}

core/endorser/endorser.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ func (e *Endorser) callChaincode(ctxt context.Context, cis *pb.ChaincodeInvocati
126126
if txsim, err = e.getTxSimulator(chainName); err != nil {
127127
return nil, nil, err
128128
}
129+
130+
defer txsim.Done()
131+
129132
ctxt = context.WithValue(ctxt, chaincode.TXSimulatorKey, txsim)
130133
b, err = chaincode.ExecuteChaincode(ctxt, pb.Transaction_CHAINCODE_INVOKE, chainName, cis.ChaincodeSpec.ChaincodeID.Name, cis.ChaincodeSpec.CtorMsg.Args)
131134

@@ -215,7 +218,7 @@ func (e *Endorser) ProcessProposal(ctx context.Context, prop *pb.Proposal) (*pb.
215218
//1 -- simulate
216219
//TODO what do we do with response ? We need it for Invoke responses for sure
217220
//Which field in PayloadResponse will carry return value ?
218-
_, simulationResult, err := e.simulateProposal(ctx, prop)
221+
payload, simulationResult, err := e.simulateProposal(ctx, prop)
219222
if err != nil {
220223
return &pb.ProposalResponse{Response: &pb.Response2{Status: 500, Message: err.Error()}}, err
221224
}
@@ -237,7 +240,7 @@ func (e *Endorser) ProcessProposal(ctx context.Context, prop *pb.Proposal) (*pb.
237240
}
238241

239242
//TODO when we have additional field in response, use "resp" bytes from the simulation
240-
resp := &pb.Response2{Status: 200, Message: "Proposal accepted"}
243+
resp := &pb.Response2{Status: 200, Message: "Proposal accepted", Payload: payload}
241244

242245
return &pb.ProposalResponse{Response: resp, ActionBytes: actionBytes, Endorsement: endorsement}, nil
243246
}

core/system_chaincode/lccc/lccc.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ const (
5151

5252
//GETCCINFO get chaincode
5353
GETCCINFO = "getid"
54+
55+
//GETDEPSPEC get ChaincodeDeploymentSpec
56+
GETDEPSPEC = "getdepspec"
5457
)
5558

5659
//---------- the LCCC -----------------
@@ -333,7 +336,7 @@ func (lccc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chai
333336
*}
334337
**/
335338

336-
_, err = lccc.createChaincode(stub, chainname, cds.ChaincodeSpec.ChaincodeID.Name, cds.CodePackage)
339+
_, err = lccc.createChaincode(stub, chainname, cds.ChaincodeSpec.ChaincodeID.Name, code)
337340

338341
return err
339342
}
@@ -383,7 +386,7 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) ([]byte, er
383386
err := lccc.executeDeploy(stub, chainname, code)
384387

385388
return nil, err
386-
case GETCCINFO:
389+
case GETCCINFO, GETDEPSPEC:
387390
if len(args) != 3 {
388391
return nil, InvalidArgsLenErr(len(args))
389392
}
@@ -398,7 +401,10 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) ([]byte, er
398401
return nil, TXNotFoundErr(chain + "/" + ccname)
399402
}
400403

401-
return []byte(ccrow.Columns[1].GetString_()), nil
404+
if function == GETCCINFO {
405+
return []byte(ccrow.Columns[1].GetString_()), nil
406+
}
407+
return ccrow.Columns[2].GetBytes(), nil
402408
}
403409

404410
return nil, InvalidFunctionErr(function)

0 commit comments

Comments
 (0)