Skip to content

Commit 3ac1bd3

Browse files
committed
FAB-1129 Add cc return value to proposal response
https://jira.hyperledger.org/browse/FAB-1129 The return value from the chaincode invocation was not set by the endorser in the payload of the Response message. This has been corrected. Change-Id: I580e18d1813bc3447ef9900737ca3095746c3414 Signed-off-by: Alessandro Sorniotti <[email protected]>
1 parent 82e72f4 commit 3ac1bd3

File tree

3 files changed

+247
-1
lines changed

3 files changed

+247
-1
lines changed

core/endorser/endorser.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ func (e *Endorser) ProcessProposal(ctx context.Context, prop *pb.Proposal) (*pb.
343343
//1 -- simulate
344344
//TODO what do we do with response ? We need it for Invoke responses for sure
345345
//Which field in PayloadResponse will carry return value ?
346-
_, simulationResult, ccevent, err := e.simulateProposal(ctx, prop, hdrExt.ChaincodeID, txsim)
346+
result, simulationResult, ccevent, err := e.simulateProposal(ctx, prop, hdrExt.ChaincodeID, txsim)
347347
if err != nil {
348348
return &pb.ProposalResponse{Response: &pb.Response2{Status: 500, Message: err.Error()}}, err
349349
}
@@ -361,6 +361,11 @@ func (e *Endorser) ProcessProposal(ctx context.Context, prop *pb.Proposal) (*pb.
361361
return nil, err
362362
}
363363

364+
// Set the proposal response payload - it
365+
// contains the "return value" from the
366+
// chaincode invocation
367+
pResp.Response.Payload = result
368+
364369
return pResp, nil
365370
}
366371

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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 main
18+
19+
//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of
20+
//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has
21+
//to be modified as well with the new ID of chaincode_example02.
22+
//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of
23+
//hard-coding.
24+
25+
import (
26+
"errors"
27+
"fmt"
28+
"strconv"
29+
30+
"github.com/hyperledger/fabric/core/chaincode/shim"
31+
)
32+
33+
// SimpleChaincode example simple Chaincode implementation
34+
type SimpleChaincode struct {
35+
}
36+
37+
// Init method of chaincode
38+
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) ([]byte, error) {
39+
_, args := stub.GetFunctionAndParameters()
40+
var A, B string // Entities
41+
var Aval, Bval int // Asset holdings
42+
var err error
43+
44+
if len(args) != 4 {
45+
return nil, errors.New("Incorrect number of arguments. Expecting 4")
46+
}
47+
48+
// Initialize the chaincode
49+
A = args[0]
50+
Aval, err = strconv.Atoi(args[1])
51+
if err != nil {
52+
return nil, errors.New("Expecting integer value for asset holding")
53+
}
54+
B = args[2]
55+
Bval, err = strconv.Atoi(args[3])
56+
if err != nil {
57+
return nil, errors.New("Expecting integer value for asset holding")
58+
}
59+
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
60+
61+
// Write the state to the ledger
62+
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
63+
if err != nil {
64+
return nil, err
65+
}
66+
67+
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
return []byte("OK"), nil
73+
}
74+
75+
// Invoke transaction makes payment of X units from A to B
76+
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
77+
_, args := stub.GetFunctionAndParameters()
78+
79+
var A, B string // Entities
80+
var Aval, Bval int // Asset holdings
81+
var X int // Transaction value
82+
var err error
83+
84+
if len(args) != 3 {
85+
return nil, errors.New("Incorrect number of arguments. Expecting 3")
86+
}
87+
88+
A = args[0]
89+
B = args[1]
90+
91+
// Get the state from the ledger
92+
// TODO: will be nice to have a GetAllState call to ledger
93+
Avalbytes, err := stub.GetState(A)
94+
if err != nil {
95+
return nil, errors.New("Failed to get state")
96+
}
97+
if Avalbytes == nil {
98+
return nil, errors.New("Entity not found")
99+
}
100+
Aval, _ = strconv.Atoi(string(Avalbytes))
101+
102+
Bvalbytes, err := stub.GetState(B)
103+
if err != nil {
104+
return nil, errors.New("Failed to get state")
105+
}
106+
if Bvalbytes == nil {
107+
return nil, errors.New("Entity not found")
108+
}
109+
Bval, _ = strconv.Atoi(string(Bvalbytes))
110+
111+
// Perform the execution
112+
X, err = strconv.Atoi(args[2])
113+
if err != nil {
114+
return nil, errors.New("Invalid transaction amount, expecting a integer value")
115+
}
116+
Aval = Aval - X
117+
Bval = Bval + X
118+
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
119+
120+
// Write the state back to the ledger
121+
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
122+
if err != nil {
123+
return nil, err
124+
}
125+
126+
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
127+
if err != nil {
128+
return nil, err
129+
}
130+
131+
return []byte(fmt.Sprintf("{%d,%d}", Aval, Bval)), nil
132+
}
133+
134+
// Query callback representing the query of a chaincode. Not used in 1.0 (remove when interface is removed)
135+
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface) ([]byte, error) {
136+
return nil, nil
137+
}
138+
139+
func main() {
140+
err := shim.Start(new(SimpleChaincode))
141+
if err != nil {
142+
fmt.Printf("Error starting Simple chaincode: %s", err)
143+
}
144+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
package main
17+
18+
import (
19+
"fmt"
20+
"testing"
21+
22+
"github.com/hyperledger/fabric/core/chaincode/shim"
23+
)
24+
25+
func checkInit(t *testing.T, stub *shim.MockStub, args [][]byte, retval []byte) {
26+
result, err := stub.MockInit("1", args)
27+
if err != nil {
28+
fmt.Println("Init failed", err)
29+
t.FailNow()
30+
}
31+
if retval != nil {
32+
if result == nil {
33+
fmt.Printf("Init returned nil, expected %s", string(retval))
34+
t.FailNow()
35+
}
36+
if string(result) != string(retval) {
37+
fmt.Printf("Init returned %s, expected %s", string(result), string(retval))
38+
t.FailNow()
39+
}
40+
}
41+
}
42+
43+
func checkState(t *testing.T, stub *shim.MockStub, name string, value string) {
44+
bytes := stub.State[name]
45+
if bytes == nil {
46+
fmt.Println("State", name, "failed to get value")
47+
t.FailNow()
48+
}
49+
if string(bytes) != value {
50+
fmt.Println("State value", name, "was not", value, "as expected")
51+
t.FailNow()
52+
}
53+
}
54+
55+
func checkInvoke(t *testing.T, stub *shim.MockStub, args [][]byte, retval []byte) {
56+
result, err := stub.MockInvoke("1", args)
57+
if err != nil {
58+
fmt.Println("Invoke", args, "failed", err)
59+
t.FailNow()
60+
}
61+
62+
if retval != nil {
63+
if result == nil {
64+
fmt.Printf("Invoke returned nil, expected %s", string(retval))
65+
t.FailNow()
66+
}
67+
if string(result) != string(retval) {
68+
fmt.Printf("Invoke returned %s, expected %s", string(result), string(retval))
69+
t.FailNow()
70+
}
71+
}
72+
}
73+
74+
func Test_Init(t *testing.T) {
75+
scc := new(SimpleChaincode)
76+
stub := shim.NewMockStub("ex02", scc)
77+
78+
// Init A=123 B=234
79+
checkInit(t, stub, [][]byte{[]byte("init"), []byte("A"), []byte("123"), []byte("B"), []byte("234")}, []byte("OK"))
80+
81+
checkState(t, stub, "A", "123")
82+
checkState(t, stub, "B", "234")
83+
}
84+
85+
func Test_Invoke(t *testing.T) {
86+
scc := new(SimpleChaincode)
87+
stub := shim.NewMockStub("ex02", scc)
88+
89+
// Init A=567 B=678
90+
checkInit(t, stub, [][]byte{[]byte("init"), []byte("A"), []byte("567"), []byte("B"), []byte("678")}, []byte("OK"))
91+
92+
// Invoke A->B for 123
93+
checkInvoke(t, stub, [][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("123")}, []byte("{444,801}"))
94+
95+
// Invoke B->A for 234
96+
checkInvoke(t, stub, [][]byte{[]byte("invoke"), []byte("B"), []byte("A"), []byte("234")}, []byte("{567,678}"))
97+
}

0 commit comments

Comments
 (0)