diff --git a/Makefile b/Makefile index 3a98f65aeeb..0ea26956dff 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,8 @@ orderer-docker: build/image/orderer/$(DUMMY) configtxgen: GO_TAGS+= nopkcs11 configtxgen: build/bin/configtxgen +javaenv: build/image/javaenv/$(DUMMY) + buildenv: build/image/buildenv/$(DUMMY) build/image/testenv/$(DUMMY): build/image/buildenv/$(DUMMY) diff --git a/core/chaincode/chaincodetest.yaml b/core/chaincode/chaincodetest.yaml index a31757eb44f..99bd613af13 100644 --- a/core/chaincode/chaincodetest.yaml +++ b/core/chaincode/chaincodetest.yaml @@ -139,8 +139,8 @@ logging: status: warning stop: warning login: warning - vm: warning - chaincode: warning + vm: debug + chaincode: debug ############################################################################### @@ -331,7 +331,7 @@ vm: file: /path/to/ca.pem key: file: /path/to/server-key.pem - + attachStdout: true ############################################################################### # # Chaincode section @@ -361,10 +361,24 @@ chaincode: # of platforms are expanded. For now, we can just use baseos runtime: $(BASE_DOCKER_NS)/fabric-baseos:$(ARCH)-$(BASE_VERSION) + java: + # This is an image based on java:openjdk-8 with addition compiler + # tools added for java shim layer packaging. + # This image is packed with shim layer libraries that are necessary + # for Java chaincode runtime. + Dockerfile: | + from $(DOCKER_NS)/fabric-javaenv:$(ARCH)-$(PROJECT_VERSION) + # timeout in millisecs for starting up a container and waiting for Register # to come through. 1sec should be plenty for chaincode unit tests startuptimeout: 1000 + # timeout in millisecs for invokes and initialize commands + # this timeout is used by all chaincodes in all the channels including + # system chaincodes. Default is 30000ms (30 seconds) + executetimeout: 30000 + + #timeout in millisecs for deploying chaincode from a remote repository. deploytimeout: 60000 @@ -393,6 +407,8 @@ chaincode: escc: enable vscc: enable + logLevel: debug + ############################################################################### # # Ledger section - ledger configuration encompases both the blockchain diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index 91ec368fc8a..e6a825c8aad 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -19,6 +19,7 @@ package chaincode import ( "encoding/json" "fmt" + "math/rand" "net" "os" "path/filepath" @@ -317,7 +318,7 @@ func deploy2(ctx context.Context, cccid *ccprovider.CCContext, chaincodeDeployme // Invoke a chaincode. func invoke(ctx context.Context, chainID string, spec *pb.ChaincodeSpec, blockNumber uint64) (ccevt *pb.ChaincodeEvent, uuid string, retval []byte, err error) { - return invokeWithVersion(ctx, chainID, "0", spec, blockNumber) + return invokeWithVersion(ctx, chainID, spec.GetChaincodeId().Version, spec, blockNumber) } // Invoke a chaincode with version (needed for upgrade) @@ -569,13 +570,13 @@ func checkFinalState(cccid *ccprovider.CCContext) error { } // Invoke chaincode_example02 -func invokeExample02Transaction(ctxt context.Context, cccid *ccprovider.CCContext, cID *pb.ChaincodeID, args []string, destroyImage bool) error { +func invokeExample02Transaction(ctxt context.Context, cccid *ccprovider.CCContext, cID *pb.ChaincodeID, chaincodeType pb.ChaincodeSpec_Type, args []string, destroyImage bool) error { var nextBlockNumber uint64 f := "init" argsDeploy := util.ToChaincodeArgs(f, "a", "100", "b", "200") - spec := &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: argsDeploy}} + spec := &pb.ChaincodeSpec{Type: chaincodeType, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: argsDeploy}} _, err := deploy(ctxt, cccid, spec, nextBlockNumber) nextBlockNumber++ ccID := spec.ChaincodeId.Name @@ -598,7 +599,7 @@ func invokeExample02Transaction(ctxt context.Context, cccid *ccprovider.CCContex f = "invoke" invokeArgs := append([]string{f}, args...) - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs(invokeArgs...)}} + spec = &pb.ChaincodeSpec{ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs(invokeArgs...)}} _, uuid, _, err := invoke(ctxt, cccid.ChainID, spec, nextBlockNumber) nextBlockNumber++ if err != nil { @@ -614,7 +615,7 @@ func invokeExample02Transaction(ctxt context.Context, cccid *ccprovider.CCContex // Test for delete state f = "delete" delArgs := util.ToChaincodeArgs(f, "a") - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: delArgs}} + spec = &pb.ChaincodeSpec{ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: delArgs}} _, _, _, err = invoke(ctxt, cccid.ChainID, spec, nextBlockNumber) if err != nil { return fmt.Errorf("Error deleting state in <%s>: %s", cccid.Name, err) @@ -623,34 +624,56 @@ func invokeExample02Transaction(ctxt context.Context, cccid *ccprovider.CCContex return nil } +const ( + chaincodeExample02GolangPath = "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02" + chaincodeExample02JavaPath = "../../examples/chaincode/java/chaincode_example02" + chaincodeExample06JavaPath = "../../examples/chaincode/java/chaincode_example06" +) + func TestExecuteInvokeTransaction(t *testing.T) { - chainID := util.GetTestChainID() - lis, err := initPeer(chainID) - if err != nil { - t.Fail() - t.Logf("Error creating peer: %s", err) + testCases := []struct { + chaincodeType pb.ChaincodeSpec_Type + chaincodePath string + }{ + {pb.ChaincodeSpec_GOLANG, chaincodeExample02GolangPath}, + {pb.ChaincodeSpec_JAVA, chaincodeExample02JavaPath}, } - defer finitPeer(lis, chainID) + for _, tc := range testCases { + t.Run(tc.chaincodeType.String(), func(t *testing.T) { - var ctxt = context.Background() + chainID := util.GetTestChainID() - cccid := ccprovider.NewCCContext(chainID, "example02", "0", "", false, nil, nil) - url := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02" - ccID := &pb.ChaincodeID{Name: "example02", Path: url, Version: "0"} + lis, err := initPeer(chainID) + if err != nil { + t.Fail() + t.Logf("Error creating peer: %s", err) + } - args := []string{"a", "b", "10"} - err = invokeExample02Transaction(ctxt, cccid, ccID, args, true) - if err != nil { - t.Fail() - t.Logf("Error invoking transaction: %s", err) - } else { - fmt.Print("Invoke test passed\n") - t.Log("Invoke test passed") + defer finitPeer(lis, chainID) + + var ctxt = context.Background() + chaincodeName := generateChaincodeName(tc.chaincodeType) + chaincodeVersion := "1.0.0.0" + cccid := ccprovider.NewCCContext(chainID, chaincodeName, chaincodeVersion, "", false, nil, nil) + ccID := &pb.ChaincodeID{Name: chaincodeName, Path: tc.chaincodePath, Version: chaincodeVersion} + + args := []string{"a", "b", "10"} + err = invokeExample02Transaction(ctxt, cccid, ccID, tc.chaincodeType, args, true) + if err != nil { + t.Fail() + t.Logf("Error invoking transaction: %s", err) + } else { + fmt.Print("Invoke test passed\n") + t.Log("Invoke test passed") + } + + theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: ccID}}) + + }) } - theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: ccID}}) } // Test the execution of an invalid transaction. @@ -674,7 +697,7 @@ func TestExecuteInvokeInvalidTransaction(t *testing.T) { //FAIL, FAIL! args := []string{"x", "-1"} - err = invokeExample02Transaction(ctxt, cccid, ccID, args, false) + err = invokeExample02Transaction(ctxt, cccid, ccID, pb.ChaincodeSpec_GOLANG, args, false) //this HAS to fail with expectedDeltaStringPrefix if err != nil { @@ -1509,6 +1532,76 @@ func TestChaincodeInvokesSystemChaincode(t *testing.T) { theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec}) } +func TestChaincodeInitializeInitError(t *testing.T) { + testCases := []struct { + name string + chaincodeType pb.ChaincodeSpec_Type + chaincodePath string + args []string + }{ + {"NotSuccessResponse", pb.ChaincodeSpec_GOLANG, chaincodeExample02GolangPath, []string{"init", "not", "enough", "args"}}, + {"NotSuccessResponse", pb.ChaincodeSpec_JAVA, chaincodeExample02JavaPath, []string{"init", "not", "enough", "args"}}, + {"RuntimeException", pb.ChaincodeSpec_JAVA, chaincodeExample06JavaPath, []string{"runtimeException"}}, + } + + channelID := util.GetTestChainID() + + for _, tc := range testCases { + t.Run(tc.name+"_"+tc.chaincodeType.String(), func(t *testing.T) { + + // initialize peer + if listener, err := initPeer(channelID); err != nil { + t.Errorf("Error creating peer: %s", err) + } else { + defer finitPeer(listener, channelID) + } + + var nextBlockNumber uint64 + + // the chaincode to install and instanciate + chaincodeName := generateChaincodeName(tc.chaincodeType) + chaincodePath := tc.chaincodePath + chaincodeVersion := "1.0.0.0" + chaincodeType := tc.chaincodeType + chaincodeDeployArgs := util.ArrayToChaincodeArgs(tc.args) + + // new chaincode context for passing around parameters + chaincodeCtx := ccprovider.NewCCContext(channelID, chaincodeName, chaincodeVersion, "", false, nil, nil) + + // attempt to deploy chaincode + _, err := deployChaincode(context.Background(), chaincodeCtx, chaincodeType, chaincodePath, chaincodeDeployArgs, nextBlockNumber) + + // deploy should of failed + if err == nil { + t.Fatal("Deployment should have failed.") + } + t.Log(err) + + }) + } +} + +func deployChaincode(ctx context.Context, chaincodeCtx *ccprovider.CCContext, chaincodeType pb.ChaincodeSpec_Type, path string, args [][]byte, nextBlockNumber uint64) ([]byte, error) { + + chaincodeSpec := &pb.ChaincodeSpec{ + ChaincodeId: &pb.ChaincodeID{ + Name: chaincodeCtx.Name, + Version: chaincodeCtx.Version, + Path: path, + }, + Type: chaincodeType, + Input: &pb.ChaincodeInput{ + Args: args, + }, + } + + result, err := deploy(ctx, chaincodeCtx, chaincodeSpec, nextBlockNumber) + if err != nil { + return nil, fmt.Errorf("Error deploying <%s:%s>: %s", chaincodeSpec.ChaincodeId.Name, chaincodeSpec.ChaincodeId.Version, err) + } + return result, nil +} + var signer msp.SigningIdentity func TestMain(m *testing.M) { @@ -1527,3 +1620,18 @@ func TestMain(m *testing.M) { SetupTestConfig() os.Exit(m.Run()) } + +var rng *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) + +func generateChaincodeName(chaincodeType pb.ChaincodeSpec_Type) string { + prefix := "cc_" + switch chaincodeType { + case pb.ChaincodeSpec_GOLANG: + prefix = "cc_go_" + case pb.ChaincodeSpec_JAVA: + prefix = "cc_java_" + case pb.ChaincodeSpec_NODE: + prefix = "cc_js_" + } + return fmt.Sprintf("%s%06d", prefix, rng.Intn(999999)) +} diff --git a/core/chaincode/multichains_test.go b/core/chaincode/multichains_test.go index 3c140418121..6090f453993 100644 --- a/core/chaincode/multichains_test.go +++ b/core/chaincode/multichains_test.go @@ -44,7 +44,7 @@ func TestExecuteInvokeOnManyChains(t *testing.T) { args := []string{"a", "b", "10"} for _, c := range chains { cccid := ccprovider.NewCCContext(c, "example02", "0", "", false, nil, nil) - err = invokeExample02Transaction(ctxt, cccid, chaincodeID, args, false) + err = invokeExample02Transaction(ctxt, cccid, chaincodeID, pb.ChaincodeSpec_GOLANG, args, false) if err != nil { t.Fail() t.Logf("Error invoking transaction: %s", err) diff --git a/core/chaincode/shim/java/build.gradle b/core/chaincode/shim/java/build.gradle index 76a1fe207b1..4d9719f15dd 100644 --- a/core/chaincode/shim/java/build.gradle +++ b/core/chaincode/shim/java/build.gradle @@ -89,16 +89,21 @@ task copyToLib(type: Copy) { task copyProtos(type:Copy){ - - from ("${rootDir}/protos/peer"){ - include '**/chaincode_event.proto' - include '**/chaincode.proto' - include '**/chaincode_shim.proto' - include '**/proposal.proto' - include '**/proposal_response.proto' - } - into "${projectDir}/src/main/proto/peer" - + into "peer", { + from ("${rootDir}/protos/peer"){ + include 'chaincode_event.proto' + include 'chaincode.proto' + include 'chaincode_shim.proto' + include 'proposal.proto' + include 'proposal_response.proto' + } + } + into "common", { + from ("${rootDir}/protos/common"){ + include 'common.proto' + } + } + into "${projectDir}/src/main/proto" } tasks['build'].mustRunAfter tasks['copyProtos'] @@ -108,6 +113,7 @@ build.finalizedBy(publishToMavenLocal) dependencies { compile 'com.google.protobuf:protobuf-java:3.0.0' + compile 'com.google.protobuf:protobuf-java-util:3.0.0' compile 'io.grpc:grpc-all:0.13.2' compile 'commons-cli:commons-cli:1.3.1' compile 'io.netty:netty-tcnative-boringssl-static:1.1.33.Fork21:' + tcnative_classifier diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeBase.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeBase.java index 9a9d4da9828..5dbdfe4ba41 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeBase.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeBase.java @@ -26,12 +26,14 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeID; -import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc; -import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc.ChaincodeSupportStub; import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type; +import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc; +import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc.ChaincodeSupportStub; +import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; -import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; import io.grpc.ManagedChannel; import io.grpc.netty.GrpcSslContexts; @@ -42,209 +44,197 @@ public abstract class ChaincodeBase { - private static Log logger = LogFactory.getLog(ChaincodeBase.class); - - public abstract String run(ChaincodeStub stub, String function, String[] args); -// public abstract String query(ChaincodeStub stub, String function, String[] args); - public abstract String getChaincodeID(); - - public static final String DEFAULT_HOST = "127.0.0.1"; - public static final int DEFAULT_PORT = 7051; - - private String host = DEFAULT_HOST; - private int port = DEFAULT_PORT; - private String hostOverrideAuthority = ""; - private static final String ROOTCERT_PEM = "/root/certs/rootcert.pem"; - private boolean tlsEnabled=false; - - private Handler handler; - private String id = getChaincodeID(); - - private final static String CORE_CHAINCODE_ID_NAME = "CORE_CHAINCODE_ID_NAME"; - private final static String CORE_PEER_ADDRESS = "CORE_PEER_ADDRESS"; - private final static String CORE_PEER_TLS_ENABLED = "CORE_PEER_TLS_ENABLED"; - private final static String CORE_PEER_TLS_SERVERHOSTOVERRIDE = "CORE_PEER_TLS_SERVERHOSTOVERRIDE"; - - // Start entry point for chaincodes bootstrap. - public void start(String[] args) { - - processEnvironmentOptions(); - processCommandLineOptions(args); - - Runnable chaincode = () -> { - logger.trace("chaincode started"); - ManagedChannel connection = newPeerClientConnection(); - logger.trace("connection created"); - chatWithPeer(connection); - logger.trace("chatWithPeer DONE"); - }; - new Thread(chaincode).start(); - } - - private void processCommandLineOptions(String[] args) { - Options options = new Options(); - options.addOption("a", "peerAddress", true, "Address of peer to connect to"); - options.addOption("s", "securityEnabled", false, "Present if security is enabled"); - options.addOption("i", "id", true, "Identity of chaincode"); - options.addOption("o", "hostNameOverride", true, "Hostname override for server certificate"); - try { - CommandLine cl = new DefaultParser().parse(options, args); - if (cl.hasOption('a')) { - host = cl.getOptionValue('a'); - port = new Integer(host.split(":")[1]); - host = host.split(":")[0]; - } - if (cl.hasOption('s')) { - tlsEnabled = true; - logger.debug("TLS enabled"); - if (cl.hasOption('o')){ - hostOverrideAuthority = cl.getOptionValue('o'); - logger.debug("server host override given " + hostOverrideAuthority); - } - } - if (cl.hasOption('i')) { - id = cl.getOptionValue('i'); - } - } catch (Exception e) { - logger.warn("cli parsing failed with exception",e); - + private static Log logger = LogFactory.getLog(ChaincodeBase.class); + + public abstract Response run(ChaincodeStub stub, String function, String[] args); + + public abstract String getChaincodeID(); + + public static final String DEFAULT_HOST = "127.0.0.1"; + public static final int DEFAULT_PORT = 7051; + + private String host = DEFAULT_HOST; + private int port = DEFAULT_PORT; + private String hostOverrideAuthority = ""; + private boolean tlsEnabled = false; + private String rootCertFile = "/etc/hyperledger/fabric/peer.crt"; + + private Handler handler; + private String id = getChaincodeID(); + + private final static String CORE_CHAINCODE_ID_NAME = "CORE_CHAINCODE_ID_NAME"; + private final static String CORE_PEER_ADDRESS = "CORE_PEER_ADDRESS"; + private final static String CORE_PEER_TLS_ENABLED = "CORE_PEER_TLS_ENABLED"; + private final static String CORE_PEER_TLS_SERVERHOSTOVERRIDE = "CORE_PEER_TLS_SERVERHOSTOVERRIDE"; + private static final String CORE_PEER_TLS_ROOTCERT_FILE = "CORE_PEER_TLS_ROOTCERT_FILE"; + + // Start entry point for chaincodes bootstrap. + public void start(String[] args) { + processEnvironmentOptions(); + processCommandLineOptions(args); + new Thread(() -> { + logger.trace("chaincode started"); + final ManagedChannel connection = newPeerClientConnection(); + logger.trace("connection created"); + chatWithPeer(connection); + logger.trace("chatWithPeer DONE"); + }).start(); + } + + private void processCommandLineOptions(String[] args) { + Options options = new Options(); + options.addOption("a", "peerAddress", true, "Address of peer to connect to"); + options.addOption("s", "securityEnabled", false, "Present if security is enabled"); + options.addOption("i", "id", true, "Identity of chaincode"); + options.addOption("o", "hostNameOverride", true, "Hostname override for server certificate"); + try { + CommandLine cl = new DefaultParser().parse(options, args); + if (cl.hasOption('a')) { + host = cl.getOptionValue('a'); + port = new Integer(host.split(":")[1]); + host = host.split(":")[0]; } - } - - private void processEnvironmentOptions() { - if(System.getenv().containsKey(CORE_CHAINCODE_ID_NAME)) { - this.id = System.getenv(CORE_CHAINCODE_ID_NAME); + if (cl.hasOption('s')) { + tlsEnabled = true; + logger.info("TLS enabled"); + if (cl.hasOption('o')) { + hostOverrideAuthority = cl.getOptionValue('o'); + logger.info("server host override given " + hostOverrideAuthority); + } } - if(System.getenv().containsKey(CORE_PEER_ADDRESS)) { - this.host = System.getenv(CORE_PEER_ADDRESS); + if (cl.hasOption('i')) { + id = cl.getOptionValue('i'); } - if(System.getenv().containsKey(CORE_PEER_TLS_ENABLED)) { - this.tlsEnabled = Boolean.parseBoolean(System.getenv(CORE_PEER_TLS_ENABLED)); - if(System.getenv().containsKey(CORE_PEER_TLS_SERVERHOSTOVERRIDE)) { - this.hostOverrideAuthority = System.getenv(CORE_PEER_TLS_SERVERHOSTOVERRIDE); + } catch (Exception e) { + logger.warn("cli parsing failed with exception", e); + + } + } + + private void processEnvironmentOptions() { + if (System.getenv().containsKey(CORE_CHAINCODE_ID_NAME)) { + this.id = System.getenv(CORE_CHAINCODE_ID_NAME); + } + if (System.getenv().containsKey(CORE_PEER_ADDRESS)) { + this.host = System.getenv(CORE_PEER_ADDRESS); + } + if (System.getenv().containsKey(CORE_PEER_TLS_ENABLED)) { + this.tlsEnabled = Boolean.parseBoolean(System.getenv(CORE_PEER_TLS_ENABLED)); + if (System.getenv().containsKey(CORE_PEER_TLS_SERVERHOSTOVERRIDE)) { + this.hostOverrideAuthority = System.getenv(CORE_PEER_TLS_SERVERHOSTOVERRIDE); + } + if (System.getenv().containsKey(CORE_PEER_TLS_ROOTCERT_FILE)) { + this.rootCertFile = System.getenv(CORE_PEER_TLS_ROOTCERT_FILE); + } + } + } + + public ManagedChannel newPeerClientConnection() { + final NettyChannelBuilder builder = NettyChannelBuilder.forAddress(host, port); + logger.info("Configuring channel connection to peer."); + + if (tlsEnabled) { + logger.info("TLS is enabled"); + try { + final SslContext sslContext = GrpcSslContexts.forClient().trustManager(new File(this.rootCertFile)).build(); + builder.negotiationType(NegotiationType.TLS); + if (!hostOverrideAuthority.equals("")) { + logger.info("Host override " + hostOverrideAuthority); + builder.overrideAuthority(hostOverrideAuthority); } + builder.sslContext(sslContext); + logger.info("TLS context built: " + sslContext); + } catch (SSLException e) { + logger.error("failed connect to peer with SSLException", e); } + } else { + builder.usePlaintext(true); } - - public ManagedChannel newPeerClientConnection() { - NettyChannelBuilder builder = NettyChannelBuilder.forAddress(host, port); - logger.info("Inside newPeerCLientConnection"); - - if (tlsEnabled) { - logger.info("tls enable"); - try { - SslContext sslContext = GrpcSslContexts.forClient() - .trustManager(new File(ROOTCERT_PEM)) - .build(); - builder.negotiationType(NegotiationType.TLS); - if (!hostOverrideAuthority.equals("")){ - logger.info("host override " + hostOverrideAuthority); - builder.overrideAuthority(hostOverrideAuthority); - } - builder.sslContext(sslContext); - logger.info("context built" + sslContext); - } catch (SSLException e) { - logger.error("failed connect to peer with SSLException",e); - } - } else { - builder.usePlaintext(true); + return builder.build(); + } + + public void chatWithPeer(ManagedChannel connection) { + // Establish stream with validating peer + ChaincodeSupportStub stub = ChaincodeSupportGrpc.newStub(connection); + + logger.info("Connecting to peer."); + + StreamObserver requestObserver = null; + try { + requestObserver = stub.register(new StreamObserver() { + + @Override + public void onNext(ChaincodeMessage message) { + logger.info("Got message from peer: " + toJsonString(message)); + try { + logger.info(String.format("[%s]Received message %s from org.hyperledger.java.shim", + Handler.shortID(message.getTxid()), message.getType())); + handler.handleMessage(message); + } catch (Exception e) { + e.printStackTrace(); + System.exit(-1); + } } - return builder.build(); - } - public void chatWithPeer(ManagedChannel connection) { - // Establish stream with validating peer - ChaincodeSupportStub stub = ChaincodeSupportGrpc.newStub(connection); - - StreamObserver requestObserver = null; - try { - requestObserver = stub.register( - new StreamObserver() { - - @Override - public void onNext(ChaincodeMessage message) { - try { - logger.debug(String.format("[%s]Received message %s from org.hyperledger.java.shim", - Handler.shortID(message.getTxid()), message.getType())); - handler.handleMessage(message); - } catch (Exception e) { - e.printStackTrace(); - System.exit(-1); - //TODO - // } else if (err != nil) { - // logger.Error(fmt.Sprintf("Received error from server: %s, ending chaincode stream", err)) - // return - // } else if (in == nil) { - // err = fmt.Errorf("Received nil message, ending chaincode stream") - // logger.debug("Received nil message, ending chaincode stream") - // return - } - } - - @Override - public void onError(Throwable e) { - logger.error("Unable to connect to peer server: "+ e.getMessage()); - System.exit(-1); - } - - @Override - public void onCompleted() { - connection.shutdown(); - handler.nextState.close(); - } - }); - } catch (Exception e) { - logger.error("Unable to connect to peer server"); - System.exit(-1); + @Override + public void onError(Throwable e) { + logger.error("Unable to connect to peer server: " + e.getMessage()); + System.exit(-1); } - // Create the org.hyperledger.java.shim handler responsible for all control logic - handler = new Handler(requestObserver, this); - - // Send the ChaincodeID during register. - ChaincodeID chaincodeID = ChaincodeID.newBuilder() - .setName(id)//TODO params.get("chaincode.id.name")) - .build(); - - ChaincodeMessage payload = ChaincodeMessage.newBuilder() - .setPayload(chaincodeID.toByteString()) - .setType(Type.REGISTER) - .build(); - - // Register on the stream - logger.debug(String.format("Registering as '%s' ... sending %s", id, Type.REGISTER)); - handler.serialSend(payload); - - while (true) { - try { - NextStateInfo nsInfo = handler.nextState.take(); - ChaincodeMessage message = nsInfo.message; - handler.handleMessage(message); - //keepalive messages are PONGs to the fabric's PINGs - if (nsInfo.sendToCC || message.getType() == Type.KEEPALIVE) { - if (message.getType() == Type.KEEPALIVE){ - logger.debug("Sending KEEPALIVE response"); - }else { - logger.debug("[" + Handler.shortID(message.getTxid()) + "]Send state message " + message.getType()); - } - handler.serialSend(message); - } - } catch (Exception e) { - break; - } + @Override + public void onCompleted() { + connection.shutdown(); + handler.nextState.close(); } + }); + } catch (Exception e) { + logger.error("Unable to connect to peer server"); + System.exit(-1); } - public ByteString runRaw(ChaincodeStub stub, String function, String[] args) { - return null; - } + // Create the org.hyperledger.java.shim handler responsible for all + // control logic + handler = new Handler(requestObserver, this); - protected ByteString runHelper(ChaincodeStub stub, String function, String[] args) { - ByteString ret = runRaw(stub, function, args); - if (ret == null) { - String tmp = run(stub, function, args); - ret = ByteString.copyFromUtf8(tmp == null ? "" : tmp); + // Send the ChaincodeID during register. + ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(id)// TODO + // params.get("chaincode.id.name")) + .build(); + + ChaincodeMessage payload = ChaincodeMessage.newBuilder().setPayload(chaincodeID.toByteString()) + .setType(Type.REGISTER).build(); + + // Register on the stream + logger.info(String.format("Registering as '%s' ... sending %s", id, Type.REGISTER)); + handler.serialSend(payload); + + while (true) { + try { + NextStateInfo nsInfo = handler.nextState.take(); + ChaincodeMessage message = nsInfo.message; + handler.handleMessage(message); + // keepalive messages are PONGs to the fabric's PINGs + if (nsInfo.sendToCC || message.getType() == Type.KEEPALIVE) { + if (message.getType() == Type.KEEPALIVE) { + logger.info("Sending KEEPALIVE response"); + } else { + logger.info( + "[" + Handler.shortID(message.getTxid()) + "]Send state message " + message.getType()); + } + handler.serialSend(message); } - return ret; + } catch (Exception e) { + break; + } } + } + + static String toJsonString(ChaincodeMessage message) { + try { + return JsonFormat.printer().print(message); + } catch(InvalidProtocolBufferException e) { + return String.format("{ Type: %s, TxId: %s }", message.getType(), message.getTxid()); + } + } } diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeHelper.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeHelper.java new file mode 100644 index 00000000000..162394dd1dd --- /dev/null +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeHelper.java @@ -0,0 +1,172 @@ +/* + * Copyright 2017 IBM - All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hyperledger.java.shim; + +import static org.hyperledger.fabric.protos.common.Common.Status.BAD_REQUEST; +import static org.hyperledger.fabric.protos.common.Common.Status.FORBIDDEN; +import static org.hyperledger.fabric.protos.common.Common.Status.INTERNAL_SERVER_ERROR; +import static org.hyperledger.fabric.protos.common.Common.Status.NOT_FOUND; +import static org.hyperledger.fabric.protos.common.Common.Status.REQUEST_ENTITY_TOO_LARGE; +import static org.hyperledger.fabric.protos.common.Common.Status.SERVICE_UNAVAILABLE; +import static org.hyperledger.fabric.protos.common.Common.Status.SUCCESS; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +import org.hyperledger.fabric.protos.common.Common.Status; +import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; +import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response.Builder; + +import com.google.protobuf.ByteString; + +public abstract class ChaincodeHelper { + public static final int UNKNOWN_VALUE = 0; + + public static Response newResponse(Status status, String message, byte[] payload) { + final Builder builder = Response.newBuilder(); + builder.setStatus(status.getNumber()); + if(message != null) { + builder.setMessage(message); + } + if(payload != null) { + builder.setPayload(ByteString.copyFrom(payload)); + } + return builder.build(); + } + + public static Response newSuccessResponse(String message, byte[] payload) { + return newResponse(SUCCESS, message, payload); + } + + public static Response newSuccessResponse() { + return newSuccessResponse(null, null); + } + + public static Response newSuccessResponse(String message) { + return newSuccessResponse(message, null); + } + + public static Response newSuccessResponse(byte[] payload) { + return newSuccessResponse(null, payload); + } + + public static Response newBadRequestResponse(String message, byte[] payload) { + return newResponse(BAD_REQUEST, message, payload); + } + + public static Response newBadRequestResponse() { + return newBadRequestResponse(null, null); + } + + public static Response newBadRequestResponse(String message) { + return newBadRequestResponse(message, null); + } + + public static Response newBadRequestResponse(byte[] payload) { + return newBadRequestResponse(null, payload); + } + + public static Response newForbiddenResponse(byte[] payload) { + return newForbiddenResponse(null, payload); + } + + public static Response newForbiddenResponse(String message, byte[] payload) { + return newResponse(FORBIDDEN, message, payload); + } + + public static Response newForbiddenResponse() { + return newForbiddenResponse(null, null); + } + + public static Response newForbiddenResponse(String message) { + return newForbiddenResponse(message, null); + } + + public static Response newNotFoundResponse(String message, byte[] payload) { + return newResponse(NOT_FOUND, message, payload); + } + + public static Response newNotFoundResponse() { + return newNotFoundResponse(null, null); + } + + public static Response newNotFoundResponse(String message) { + return newNotFoundResponse(message, null); + } + + public static Response newNotFoundResponse(byte[] payload) { + return newNotFoundResponse(null, payload); + } + + public static Response newRequestEntityTooLargeResponse(String message, byte[] payload) { + return newResponse(REQUEST_ENTITY_TOO_LARGE, message, payload); + } + + public static Response newRequestEntityTooLargeResponse() { + return newRequestEntityTooLargeResponse(null, null); + } + + public static Response newRequestEntityTooLargeResponse(String message) { + return newRequestEntityTooLargeResponse(message, null); + } + + public static Response newRequestEntityTooLargeResponse(byte[] payload) { + return newRequestEntityTooLargeResponse(null, payload); + } + + public static Response newInternalServerErrorResponse(String message, byte[] payload) { + return newResponse(INTERNAL_SERVER_ERROR, message, payload); + } + + public static Response newInternalServerErrorResponse() { + return newInternalServerErrorResponse(null, null); + } + + public static Response newInternalServerErrorResponse(String message) { + return newInternalServerErrorResponse(message, null); + } + + public static Response newInternalServerErrorResponse(byte[] payload) { + return newInternalServerErrorResponse(null, payload); + } + + public static Response newInternalServerErrorResponse(Throwable throwable) { + return newInternalServerErrorResponse(throwable.getMessage(), printStackTrace(throwable)); + } + + public static Response newServiceUnavailableErrorResponse(String message, byte[] payload) { + return newResponse(SERVICE_UNAVAILABLE, message, payload); + } + + public static Response newServiceUnavailableErrorResponse() { + return newServiceUnavailableErrorResponse(null, null); + } + + public static Response newServiceUnavailableErrorResponse(String message) { + return newServiceUnavailableErrorResponse(message, null); + } + + public static Response newServiceUnavailableErrorResponse(byte[] payload) { + return newServiceUnavailableErrorResponse(null, payload); + } + + static byte[] printStackTrace(Throwable throwable) { + if(throwable == null) return null; + final StringWriter buffer = new StringWriter(); + throwable.printStackTrace(new PrintWriter(buffer)); + return buffer.toString().getBytes(StandardCharsets.UTF_8); + } + +} diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java index d20dd1d4eb5..5329f70eb28 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java @@ -1,5 +1,5 @@ /* -Copyright DTCC 2016 All Rights Reserved. +Copyright DTCC, IBM 2016, 2017 All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,10 +16,36 @@ package org.hyperledger.java.shim; -import com.google.protobuf.ByteString; -import io.grpc.stub.StreamObserver; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.COMPLETED; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.DEL_STATE; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.ERROR; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.GET_STATE; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.INIT; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.INVOKE_CHAINCODE; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.PUT_STATE; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.READY; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.REGISTERED; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.RESPONSE; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.TRANSACTION; +import static org.hyperledger.java.fsm.CallbackType.AFTER_EVENT; +import static org.hyperledger.java.fsm.CallbackType.BEFORE_EVENT; +import static org.hyperledger.java.shim.HandlerHelper.newCompletedEventMessage; +import static org.hyperledger.java.shim.HandlerHelper.newErrorEventMessage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.hyperledger.fabric.protos.common.Common.Status; +import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeID; +import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeInput; +import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeSpec; +import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage; +import org.hyperledger.fabric.protos.peer.ChaincodeShim.PutStateInfo; +import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; import org.hyperledger.java.fsm.CBDesc; import org.hyperledger.java.fsm.Event; import org.hyperledger.java.fsm.EventDesc; @@ -27,17 +53,11 @@ import org.hyperledger.java.fsm.exceptions.CancelledException; import org.hyperledger.java.fsm.exceptions.NoTransitionException; import org.hyperledger.java.helper.Channel; -import org.hyperledger.fabric.protos.peer.Chaincode.*; -import org.hyperledger.fabric.protos.peer.ChaincodeShim.*; -import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Builder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; -import static org.hyperledger.java.fsm.CallbackType.*; -import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.*; +import io.grpc.stub.StreamObserver; public class Handler { @@ -100,6 +120,7 @@ public void triggerNextState(ChaincodeMessage message, boolean send) { } public synchronized void serialSend(ChaincodeMessage message) { + logger.debug("Sending message to peer: " + ChaincodeBase.toJsonString(message)); try { chatStream.onNext(message); } catch (Exception e) { @@ -182,66 +203,53 @@ public void beforeRegistered(Event event) { * @param message chaincode to be initialized */ public void handleInit(ChaincodeMessage message) { - Runnable task = () -> { - ChaincodeMessage nextStatemessage = null; - boolean send = true; + new Thread(() -> { try { + // Get the function and args from Payload - ChaincodeInput input; - try { - input = ChaincodeInput.parseFrom(message.getPayload()); - } catch (Exception e) { - // payload = []byte(unmarshalErr.Error()) - // Send ERROR message to chaincode support and change state - // logger.debug(String.format("[%s]Incorrect payload format. Sending %s", shortID(message), ERROR) - // nextStatemessage = ChaincodeMessage.newBuilder(){Type: ERROR, Payload: payload, Uuid: message.getTxid()} - return; - } - - // // Mark as a transaction (allow put/del state) + final ChaincodeInput input = ChaincodeInput.parseFrom(message.getPayload()); + + // Mark as a transaction (allow put/del state) markIsTransaction(message.getTxid(), true); - + // Create the ChaincodeStub which the chaincode can use to callback - ChaincodeStub stub = new ChaincodeStub(message.getTxid(), this); - + final ChaincodeStub stub = new ChaincodeStub(message.getTxid(), this); + // Call chaincode's Run - ByteString result; - try { - result = chaincode.runHelper(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList())); - } catch (Exception e) { - // Send ERROR message to chaincode support and change state - logger.debug(String.format("[%s]Init failed. Sending %s", shortID(message), ERROR)); - nextStatemessage = ChaincodeMessage.newBuilder() + final Response result = chaincode.run(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList())); + logger.debug(String.format(String.format("[%s]Init succeeded. Sending %s", shortID(message), COMPLETED))); + + if(result.getStatus() == Status.SUCCESS_VALUE) { + // Send COMPLETED with entire result as payload + triggerNextState(ChaincodeMessage.newBuilder() + .setType(COMPLETED) + .setPayload(result.toByteString()) + .setTxid(message.getTxid()) + .build(), true); + } else { + // Send ERROR with entire result.Message as payload + triggerNextState(ChaincodeMessage.newBuilder() .setType(ERROR) - .setPayload(ByteString.copyFromUtf8(e.getMessage())) + .setPayload(result.getMessageBytes()) .setTxid(message.getTxid()) - .build(); - return; - } finally { - // delete isTransaction entry - deleteIsTransaction(message.getTxid()); + .build(), true); } - - // Send COMPLETED message to chaincode support and change state - nextStatemessage = ChaincodeMessage.newBuilder() - .setType(COMPLETED) - .setPayload(result) + + } catch (InvalidProtocolBufferException | RuntimeException e) { + logger.error(String.format("[%s]Init failed. Sending %s", shortID(message), ERROR), e); + triggerNextState(ChaincodeMessage.newBuilder() + .setType(ERROR) + .setPayload(Response.newBuilder() + .setStatus(Status.INTERNAL_SERVER_ERROR_VALUE) + .setPayload(ByteString.copyFromUtf8(e.toString())) + .build().toByteString()) .setTxid(message.getTxid()) - .build(); - - logger.debug(String.format(String.format("[%s]Init succeeded. Sending %s", - shortID(message), COMPLETED))); - - //TODO put in all exception states - } catch (Exception e) { - throw e; + .build(), true); } finally { - triggerNextState(nextStatemessage, send); + // delete isTransaction entry + deleteIsTransaction(message.getTxid()); } - }; - - //Run above task - new Thread(task).start(); + }).start(); } private String getFunction(List args) { @@ -253,13 +261,13 @@ private String[] getParameters(List args) { // skip arg[0], that is the function name for(int i = 1; i < args.size(); i++) { results.add(args.get(i).toStringUtf8()); - } + } return results.toArray(new String[results.size()]); } // enterInitState will initialize the chaincode if entering init from established. public void beforeInit(Event event) { - logger.debug(String.format("Before %s event.", event.name)); + logger.debug(String.format("Before %s event.", event.name)); logger.debug(String.format("Current state %s", fsm.current())); final ChaincodeMessage message = extractMessageFromEvent(event); logger.debug(String.format("[%s]Received %s, initializing chaincode", shortID(message), message.getType())); @@ -269,7 +277,6 @@ public void beforeInit(Event event) { } } - // // handleTransaction Handles request to execute a transaction. public void handleTransaction(ChaincodeMessage message) { // The defer followed by triggering a go routine dance is needed to ensure that the previous state transition @@ -286,14 +293,11 @@ public void handleTransaction(ChaincodeMessage message) { ChaincodeInput input; try { input = ChaincodeInput.parseFrom(message.getPayload()); - } catch (Exception e) { + } catch (InvalidProtocolBufferException e) { + logger.error(String.format("[%s]Incorrect payload format", shortID(message)), e); logger.debug(String.format("[%s]Incorrect payload format. Sending %s", shortID(message), ERROR)); // Send ERROR message to chaincode support and change state - nextStatemessage = ChaincodeMessage.newBuilder() - .setType(ERROR) - .setPayload(message.getPayload()) - .setTxid(message.getTxid()) - .build(); + nextStatemessage = newErrorEventMessage(message.getTxid(), e); return; } @@ -301,37 +305,26 @@ public void handleTransaction(ChaincodeMessage message) { markIsTransaction(message.getTxid(), true); // Create the ChaincodeStub which the chaincode can use to callback - ChaincodeStub stub = new ChaincodeStub(message.getTxid(), this); + final ChaincodeStub stub = new ChaincodeStub(message.getTxid(), this); // Call chaincode's Run - ByteString response; + Response response; try { - response = chaincode.runHelper(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList())); - } catch (Exception e) { - e.printStackTrace(); - System.err.flush(); + response = chaincode.run(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList())); + } catch (Throwable throwable) { // Send ERROR message to chaincode support and change state - logger.error(String.format("[%s]Error running chaincode. Transaction execution failed. Sending %s", - shortID(message), ERROR)); - nextStatemessage = ChaincodeMessage.newBuilder() - .setType(ERROR) - .setPayload(message.getPayload()) - .setTxid(message.getTxid()) - .build(); + logger.error(String.format("[%s]Error running chaincode. Transaction execution failed. Sending %s", shortID(message), ERROR)); + nextStatemessage = newErrorEventMessage(message.getTxid(), throwable); return; } finally { deleteIsTransaction(message.getTxid()); } - logger.debug(String.format("[%s]Transaction completed. Sending %s", - shortID(message), COMPLETED)); + logger.info(String.format("[%s]Transaction completed. Sending %s", shortID(message), COMPLETED)); + logger.info(String.format("[%s] Response ='%s'", shortID(message), response)); // Send COMPLETED message to chaincode support and change state - Builder builder = ChaincodeMessage.newBuilder() - .setType(COMPLETED) - .setTxid(message.getTxid()); - if (response != null) builder.setPayload(response); - nextStatemessage = builder.build(); + nextStatemessage = newCompletedEventMessage(message.getTxid(), response); } finally { triggerNextState(nextStatemessage, send); } @@ -340,8 +333,7 @@ public void handleTransaction(ChaincodeMessage message) { new Thread(task).start(); } - - // enterTransactionState will execute chaincode's Run if coming from a TRANSACTION event. + // enterTransactionState will execute chaincode's Run if coming from a TRANSACTION event. public void beforeTransaction(Event event) { ChaincodeMessage message = extractMessageFromEvent(event); logger.debug(String.format("[%s]Received %s, invoking transaction on chaincode(src:%s, dst:%s)", @@ -762,19 +754,12 @@ public synchronized void handleMessage(ChaincodeMessage message) throws Exceptio return; } - logger.debug(String.format("[%s]Handling ChaincodeMessage of type: %s(state:%s)", - shortID(message), message.getType(), fsm.current())); + logger.debug(String.format("[%s]Handling ChaincodeMessage of type: %s(state:%s)", shortID(message), message.getType(), fsm.current())); if (fsm.eventCannotOccur(message.getType().toString())) { String errStr = String.format("[%s]Chaincode handler org.hyperledger.java.fsm cannot handle message (%s) with payload size (%d) while in state: %s", message.getTxid(), message.getType(), message.getPayload().size(), fsm.current()); - ByteString payload = ByteString.copyFromUtf8(errStr); - ChaincodeMessage errormessage = ChaincodeMessage.newBuilder() - .setType(ERROR) - .setPayload(payload) - .setTxid(message.getTxid()) - .build(); - serialSend(errormessage); + serialSend(newErrorEventMessage(message.getTxid(), errStr)); throw new RuntimeException(errStr); } diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/HandlerHelper.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/HandlerHelper.java new file mode 100644 index 00000000000..6c51be0f626 --- /dev/null +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/HandlerHelper.java @@ -0,0 +1,61 @@ +/* + * Copyright 2017 IBM - All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.hyperledger.java.shim; + +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.COMPLETED; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.ERROR; +import static org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type.GET_STATE; + +import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage; +import org.hyperledger.fabric.protos.peer.ChaincodeShim.ChaincodeMessage.Type; +import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; + +import com.google.protobuf.ByteString; + +abstract class HandlerHelper { + + private static ChaincodeMessage newEventMessage(final Type type, final String txid, final ByteString payload) { + return ChaincodeMessage.newBuilder() + .setType(type) + .setTxid(txid) + .setPayload(payload) + .build(); + } + + static ChaincodeMessage newGetStateEventMessage(final String txid, final String key) { + return newEventMessage(GET_STATE, txid, ByteString.copyFromUtf8(key)); + } + + static ChaincodeMessage newErrorEventMessage(final String txid, final Response payload) { + return newEventMessage(ERROR, txid, payload.toByteString()); + } + + static ChaincodeMessage newErrorEventMessage(final String txid, final Throwable throwable) { + return newErrorEventMessage(txid, ChaincodeHelper.newInternalServerErrorResponse(throwable)); + } + + static ChaincodeMessage newErrorEventMessage(final String txid, final String message) { + return newErrorEventMessage(txid, ChaincodeHelper.newInternalServerErrorResponse(message)); + } + + static ChaincodeMessage newCompletedEventMessage(final String txid, Response response) { + return ChaincodeMessage.newBuilder() + .setType(COMPLETED) + .setTxid(txid) + .setPayload(response.toByteString()) + .build(); + } + + +} diff --git a/examples/chaincode/java/SimpleSample/build.gradle b/examples/chaincode/java/SimpleSample/build.gradle index c98e18e29ce..9efe29aa4b2 100644 --- a/examples/chaincode/java/SimpleSample/build.gradle +++ b/examples/chaincode/java/SimpleSample/build.gradle @@ -80,4 +80,5 @@ dependencies { compile 'io.grpc:grpc-all:0.13.2' compile 'commons-cli:commons-cli:1.3.1' compile 'org.hyperledger:shim-client:1.0' - } + compile 'org.glassfish:javax.json:1.1.0-M1' +} diff --git a/examples/chaincode/java/SimpleSample/src/main/java/example/SimpleSample.java b/examples/chaincode/java/SimpleSample/src/main/java/example/SimpleSample.java index f00fdd010ae..2f450aca47a 100644 --- a/examples/chaincode/java/SimpleSample/src/main/java/example/SimpleSample.java +++ b/examples/chaincode/java/SimpleSample/src/main/java/example/SimpleSample.java @@ -1,164 +1,153 @@ -/* -Copyright DTCC 2016 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package example; - -import java.util.Arrays; - -import org.hyperledger.java.shim.ChaincodeBase; -import org.hyperledger.java.shim.ChaincodeStub; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - *

Classic "transfer" sample chaincode

- * (java implementation of chaincode_example02.go) - * @author Sergey Pomytkin spomytkin@gmail.com - * - */ -public class SimpleSample extends ChaincodeBase { - private static Log log = LogFactory.getLog(SimpleSample.class); - - @Override - public String run(ChaincodeStub stub, String function, String[] args) { - log.info("In run, function: " + function + ", args: " + Arrays.toString(args)); - - switch (function) { - case "init": - init(stub, function, args); - break; - case "transfer": - String re = transfer(stub, args); - System.out.println(re); - return re; - case "put": - for (int i = 0; i < args.length; i += 2) - stub.putState(args[i], args[i + 1]); - break; - case "del": - for (String arg : args) - stub.delState(arg); - break; - case "query": - return query(stub, function, args); - default: - return transfer(stub, args); - } - - return null; - } - - private String transfer(ChaincodeStub stub, String[] args) { - System.out.println("in transfer"); - if(args.length!=3){ - System.out.println("Incorrect number of arguments:"+args.length); - return "{\"Error\":\"Incorrect number of arguments. Expecting 3: from, to, amount\"}"; - } - String fromName =args[0]; - String fromAm=stub.getState(fromName); - String toName =args[1]; - String toAm=stub.getState(toName); - String am =args[2]; - int valFrom=0; - if (fromAm!=null&&!fromAm.isEmpty()){ - try{ - valFrom = Integer.parseInt(fromAm); - }catch(NumberFormatException e ){ - System.out.println("{\"Error\":\"Expecting integer value for asset holding of "+fromName+" \"}"+e); - return "{\"Error\":\"Expecting integer value for asset holding of "+fromName+" \"}"; - } - }else{ - return "{\"Error\":\"Failed to get state for " +fromName + "\"}"; - } - - int valTo=0; - if (toAm!=null&&!toAm.isEmpty()){ - try{ - valTo = Integer.parseInt(toAm); - }catch(NumberFormatException e ){ - e.printStackTrace(); - return "{\"Error\":\"Expecting integer value for asset holding of "+toName+" \"}"; - } - }else{ - return "{\"Error\":\"Failed to get state for " +toName + "\"}"; - } - - int valA =0; - try{ - valA = Integer.parseInt(am); - }catch(NumberFormatException e ){ - e.printStackTrace(); - return "{\"Error\":\"Expecting integer value for amount \"}"; - } - if(valA>valFrom) - return "{\"Error\":\"Insufficient asset holding value for requested transfer amount \"}"; - valFrom = valFrom-valA; - valTo = valTo+valA; - System.out.println("Transfer "+fromName+">"+toName+" am='"+am+"' new values='"+valFrom+"','"+ valTo+"'"); - stub.putState(fromName,""+ valFrom); - stub.putState(toName, ""+valTo); - - System.out.println("Transfer complete"); - - return null; - - } - - public String init(ChaincodeStub stub, String function, String[] args) { - if(args.length!=4){ - return "{\"Error\":\"Incorrect number of arguments. Expecting 4\"}"; - } - try{ - int valA = Integer.parseInt(args[1]); - int valB = Integer.parseInt(args[3]); - stub.putState(args[0], args[1]); - stub.putState(args[2], args[3]); - }catch(NumberFormatException e ){ - return "{\"Error\":\"Expecting integer value for asset holding\"}"; - } - return null; - } - - - public String query(ChaincodeStub stub, String function, String[] args) { - if(args.length!=1){ - return "{\"Error\":\"Incorrect number of arguments. Expecting name of the person to query\"}"; - } - String am =stub.getState(args[0]); - if (am!=null&&!am.isEmpty()){ - try{ - int valA = Integer.parseInt(am); - return "{\"Name\":\"" + args[0] + "\",\"Amount\":\"" + am + "\"}"; - }catch(NumberFormatException e ){ - return "{\"Error\":\"Expecting integer value for asset holding\"}"; - } }else{ - return "{\"Error\":\"Failed to get state for " + args[0] + "\"}"; - } - - - } - - @Override - public String getChaincodeID() { - return "SimpleSample"; - } - - public static void main(String[] args) throws Exception { - new SimpleSample().start(args); - } - - -} +/* +Copyright DTCC, IBM 2016, 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package example; + +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hyperledger.java.shim.ChaincodeHelper.newBadRequestResponse; +import static org.hyperledger.java.shim.ChaincodeHelper.newInternalServerErrorResponse; +import static org.hyperledger.java.shim.ChaincodeHelper.newSuccessResponse; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +import javax.json.Json; +import javax.json.JsonObjectBuilder; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; +import org.hyperledger.java.shim.ChaincodeBase; +import org.hyperledger.java.shim.ChaincodeStub; + +/** + *

Classic "transfer" sample chaincode

(java implementation of chaincode_example02.go) + * + * @author Sergey Pomytkin spomytkin@gmail.com + * + */ +public class SimpleSample extends ChaincodeBase { + private static Log log = LogFactory.getLog(SimpleSample.class); + + @Override + public Response run(ChaincodeStub stub, String function, String[] args) { + + try { + + switch (function) { + case "init": + return init(stub, function, args); + case "transfer": + return transfer(stub, args); + case "put": + for (int i = 0; i < args.length; i += 2) + stub.putState(args[i], args[i + 1]); + return newSuccessResponse(); + case "del": + for (String arg : args) + stub.delState(arg); + return newSuccessResponse(); + case "query": + return query(stub, function, args); + default: + return newBadRequestResponse(format("Unknown function: %s", function)); + } + + } catch (NumberFormatException e) { + return newBadRequestResponse(e.toString()); + } catch (IllegalArgumentException e) { + return newBadRequestResponse(e.getMessage()); + } catch (Throwable e) { + return newInternalServerErrorResponse(e); + } + + } + + private Response transfer(ChaincodeStub stub, String[] args) { + if (args.length != 3) throw new IllegalArgumentException("Incorrect number of arguments. Expecting: transfer(from, to, amount)"); + final String fromKey = args[0]; + final String toKey = args[1]; + final String amount = args[2]; + + // get state of the from/to keys + final String fromKeyState = stub.getState(fromKey); + final String toKeyState = stub.getState(toKey); + + // parse states as integers + int fromAccountBalance = Integer.parseInt(fromKeyState); + int toAccountBalance = Integer.parseInt(toKeyState); + + // parse the transfer amount as an integer + int transferAmount = Integer.parseInt(amount); + + // make sure the transfer is possible + if (transferAmount > fromAccountBalance) { + throw new IllegalArgumentException("Insufficient asset holding value for requested transfer amount."); + } + + // perform the transfer + log.info(String.format("Tranferring %d holdings from %s to %s", transferAmount, fromKey, toKey)); + int newFromAccountBalance = fromAccountBalance - transferAmount; + int newToAccountBalance = toAccountBalance + transferAmount; + log.info(String.format("New holding values will be: %s = %d, %s = %d", fromKey, newFromAccountBalance, toKey, newToAccountBalance)); + stub.putState(fromKey, Integer.toString(newFromAccountBalance)); + stub.putState(toKey, Integer.toString(newToAccountBalance)); + log.info("Transfer complete."); + + return newSuccessResponse(format("Successfully transferred %d assets from %s to %s.", transferAmount, fromKey, toKey)); + } + + public Response init(ChaincodeStub stub, String function, String[] args) { + if (args.length != 4) throw new IllegalArgumentException("Incorrect number of arguments. Expecting: init(account1, amount1, account2, amount2)"); + + final String accountKey1 = args[0]; + final String accountKey2 = args[2]; + final String account1Balance = args[1]; + final String account2Balance = args[3]; + + stub.putState(accountKey1, new Integer(account1Balance).toString()); + stub.putState(accountKey2, new Integer(account2Balance).toString()); + + return newSuccessResponse(); + } + + public Response query(ChaincodeStub stub, String function, String[] args) { + if (args.length != 1) throw new IllegalArgumentException("Incorrect number of arguments. Expecting: query(account)"); + + final String accountKey = args[0]; + + return newSuccessResponse(Json.createObjectBuilder() + .add("Name", accountKey) + .add("Amount", Integer.parseInt(stub.getState(accountKey))) + .build().toString().getBytes(UTF_8) + ); + + } + + + @Override + public String getChaincodeID() { + return "SimpleSample"; + } + + public static void main(String[] args) throws Exception { + new SimpleSample().start(args); + } + +} diff --git a/examples/chaincode/java/chaincode_example02/build.gradle b/examples/chaincode/java/chaincode_example02/build.gradle new file mode 100644 index 00000000000..8363f81c3cf --- /dev/null +++ b/examples/chaincode/java/chaincode_example02/build.gradle @@ -0,0 +1,84 @@ +/* +Copyright DTCC, IBM 2016, 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +buildscript { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } +} + +plugins { + id "java" + id "eclipse" + id "application" +} + + +task printClasspath { + doLast { + configurations.testRuntime.each { println it } + } +} + +archivesBaseName = "chaincode" +mainClassName="example.Example02" + +run { + if (project.hasProperty("appArgs")) { + args = Eval.me(appArgs) + } +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } +} + +repositories { + mavenLocal() + mavenCentral() +} + + +jar.doFirst { + destinationDir=file("${buildDir}") + manifest { + attributes ( + 'Main-Class': mainClassName, + 'Class-Path': configurations.runtime.collect { "libs/"+"$it.name" }.join(' ') + ) + } +} + +task copyToLib(type: Copy) { + into "$buildDir/libs" + from configurations.runtime +} +build.finalizedBy(copyToLib) + + +dependencies { + compile 'io.grpc:grpc-all:0.13.2' + compile 'commons-cli:commons-cli:1.3.1' + compile 'org.hyperledger:shim-client:1.0' + compile 'org.glassfish:javax.json:1.1.0-M1' +} diff --git a/examples/chaincode/java/chaincode_example02/src/main/java/example/Example02.java b/examples/chaincode/java/chaincode_example02/src/main/java/example/Example02.java new file mode 100644 index 00000000000..405e037e8c2 --- /dev/null +++ b/examples/chaincode/java/chaincode_example02/src/main/java/example/Example02.java @@ -0,0 +1,144 @@ +/* +Copyright DTCC, IBM 2016, 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package example; + +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hyperledger.java.shim.ChaincodeHelper.newBadRequestResponse; +import static org.hyperledger.java.shim.ChaincodeHelper.newInternalServerErrorResponse; +import static org.hyperledger.java.shim.ChaincodeHelper.newSuccessResponse; + +import javax.json.Json; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; +import org.hyperledger.java.shim.ChaincodeBase; +import org.hyperledger.java.shim.ChaincodeStub; + +public class Example02 extends ChaincodeBase { + + private static Log log = LogFactory.getLog(Example02.class); + + @Override + public Response run(ChaincodeStub stub, String function, String[] args) { + try { + switch (function) { + case "init": + return init(stub, args); + case "invoke": + return invoke(stub, args); + case "delete": + return delete(stub, args); + case "query": + return query(stub, args); + default: + return newBadRequestResponse(format("Unknown function: %s", function)); + } + } catch (NumberFormatException e) { + return newBadRequestResponse(e.toString()); + } catch (IllegalArgumentException e) { + return newBadRequestResponse(e.getMessage()); + } catch (Throwable e) { + return newInternalServerErrorResponse(e); + } + + } + + public Response init(ChaincodeStub stub, String[] args) { + if (args.length != 4) throw new IllegalArgumentException("Incorrect number of arguments. Expecting: init(account1, amount1, account2, amount2)"); + + final String accountKey1 = args[0]; + final String accountKey2 = args[2]; + final String account1Balance = args[1]; + final String account2Balance = args[3]; + + stub.putState(accountKey1, new Integer(account1Balance).toString()); + stub.putState(accountKey2, new Integer(account2Balance).toString()); + + return newSuccessResponse(); + } + + private Response invoke(ChaincodeStub stub, String[] args) { + if (args.length != 3) throw new IllegalArgumentException("Incorrect number of arguments. Expecting: transfer(from, to, amount)"); + + final String fromKey = args[0]; + final String toKey = args[1]; + final String amount = args[2]; + + // get state of the from/to keys + final String fromKeyState = stub.getState(fromKey); + final String toKeyState = stub.getState(toKey); + + // parse states as integers + int fromAccountBalance = Integer.parseInt(fromKeyState); + int toAccountBalance = Integer.parseInt(toKeyState); + + // parse the transfer amount as an integer + int transferAmount = Integer.parseInt(amount); + + // make sure the transfer is possible + if (transferAmount > fromAccountBalance) { + throw new IllegalArgumentException("Insufficient asset holding value for requested transfer amount."); + } + + // perform the transfer + log.info(format("Tranferring %d holdings from %s to %s", transferAmount, fromKey, toKey)); + int newFromAccountBalance = fromAccountBalance - transferAmount; + int newToAccountBalance = toAccountBalance + transferAmount; + log.info(format("New holding values will be: %s = %d, %s = %d", fromKey, newFromAccountBalance, toKey, + newToAccountBalance)); + stub.putState(fromKey, Integer.toString(newFromAccountBalance)); + stub.putState(toKey, Integer.toString(newToAccountBalance)); + log.info("Transfer complete."); + + return newSuccessResponse(format("Successfully transferred %d assets from %s to %s.", transferAmount, fromKey, toKey)); + } + + public Response delete(ChaincodeStub stub, String args[]) { + if (args.length != 1) + throw new IllegalArgumentException("Incorrect number of arguments. Expecting: delete(account)"); + + final String account = args[0]; + + stub.delState(account); + + return newSuccessResponse(); + } + + public Response query(ChaincodeStub stub, String[] args) { + if (args.length != 1) throw new IllegalArgumentException("Incorrect number of arguments. Expecting: query(account)"); + + final String accountKey = args[0]; + + return newSuccessResponse(Json.createObjectBuilder() + .add("Name", accountKey) + .add("Amount", Integer.parseInt(stub.getState(accountKey))) + .build().toString().getBytes(UTF_8)); + + } + + @Override + public String getChaincodeID() { + return "Example02"; + } + + public static void main(String[] args) throws Exception { + new Example02().start(args); + } + +} diff --git a/examples/chaincode/java/chaincode_example06/build.gradle b/examples/chaincode/java/chaincode_example06/build.gradle new file mode 100644 index 00000000000..6cd0d539a3f --- /dev/null +++ b/examples/chaincode/java/chaincode_example06/build.gradle @@ -0,0 +1,83 @@ +/* +Copyright DTCC, IBM 2016, 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +buildscript { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } +} + +plugins { + id "java" + id "eclipse" + id "application" +} + + +task printClasspath { + doLast { + configurations.testRuntime.each { println it } + } +} + +archivesBaseName = "chaincode" +mainClassName="example.Example06" + +run { + if (project.hasProperty("appArgs")) { + args = Eval.me(appArgs) + } +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } +} + +repositories { + mavenLocal() + mavenCentral() +} + + +jar.doFirst { + destinationDir=file("${buildDir}") + manifest { + attributes ( + 'Main-Class': mainClassName, + 'Class-Path': configurations.runtime.collect { "libs/"+"$it.name" }.join(' ') + ) + } +} + +task copyToLib(type: Copy) { + into "$buildDir/libs" + from configurations.runtime +} +build.finalizedBy(copyToLib) + + +dependencies { + compile 'io.grpc:grpc-all:0.13.2' + compile 'commons-cli:commons-cli:1.3.1' + compile 'org.hyperledger:shim-client:1.0' +} diff --git a/examples/chaincode/java/chaincode_example06/src/main/java/example/Example06.java b/examples/chaincode/java/chaincode_example06/src/main/java/example/Example06.java new file mode 100644 index 00000000000..74b8012f9ad --- /dev/null +++ b/examples/chaincode/java/chaincode_example06/src/main/java/example/Example06.java @@ -0,0 +1,47 @@ +/* +Copyright DTCC, IBM 2016, 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package example; + +import static java.lang.String.format; +import static org.hyperledger.java.shim.ChaincodeHelper.newBadRequestResponse; + +import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response; +import org.hyperledger.java.shim.ChaincodeBase; +import org.hyperledger.java.shim.ChaincodeStub; + +public class Example06 extends ChaincodeBase { + + @Override + public Response run(ChaincodeStub stub, String function, String[] args) { + switch (function) { + case "runtimeException": + throw new RuntimeException("Exception thrown as requested."); + default: + return newBadRequestResponse(format("Unknown function: %s", function)); + } + } + + @Override + public String getChaincodeID() { + return "Example03"; + } + + public static void main(String[] args) throws Exception { + new Example06().start(args); + } + +} diff --git a/peer/chaincode/features/environment.py b/peer/chaincode/features/environment.py deleted file mode 100644 index 9f5d0f76143..00000000000 --- a/peer/chaincode/features/environment.py +++ /dev/null @@ -1,34 +0,0 @@ -from behave import * -import os -import re -import subprocess - -# set the default step matcher -use_step_matcher("re") - -def before_all(context): - # set some handy values - context.go_path = os.environ['GOPATH'] - context.fabric_dir = os.path.join(context.go_path, 'src/github.com/hyperledger/fabric') - context.peer_exe = os.path.join(context.fabric_dir, 'build/bin/peer') - context.sample_chaincode_path = { - 'golang':'github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02', - 'java':'examples/chaincode/java/SimpleSample' - } - context.sample_chaincode_ctor_args = { - 'golang':'{"Args":["init", "a", "100", "b", "200"]}', - 'java':'{"Args": ["init", "a", "100", "b", "200"]}' - } - -def after_scenario(context, scenario): - # collect logs if failure - if context.failed: - open(re.sub('\W+', '_', scenario.name).lower() + '_peer.log', 'w').write(subprocess.check_output(['docker', 'logs', context.peer_container_id], stderr=subprocess.STDOUT)) - open(re.sub('\W+', '_', scenario.name).lower() + '_orderer.log', 'w').write(subprocess.check_output(['docker', 'logs', context.orderer_container_id], stderr=subprocess.STDOUT)) - # teardown docker containers & network - if getattr(context, 'peer_container_id', None): - subprocess.check_output(['docker', 'rm', '--force', '--volumes', context.peer_container_id], stderr=subprocess.STDOUT) - if getattr(context, 'orderer_container_id', None): - subprocess.check_output(['docker', 'rm', '--force', '--volumes', context.orderer_container_id], stderr=subprocess.STDOUT) - if getattr(context, 'network_id', None): - subprocess.check_output(['docker', 'network', 'rm', context.network_id], stderr=subprocess.STDOUT) diff --git a/peer/chaincode/features/install.feature b/peer/chaincode/features/install.feature deleted file mode 100644 index 18188bc261b..00000000000 --- a/peer/chaincode/features/install.feature +++ /dev/null @@ -1,26 +0,0 @@ -Feature: Install chaincode via CLI - -Scenario Outline: Install first version chaincode via CLI - - Given a fabric peer and orderer - When a chaincode is installed via the CLI - Then the chaincode is installed on the peer - - Examples: - | lang | - | go | - | java | - -Scenario Outline: Install chaincode with new version - - Only the first version of a chaincode (identified by chaincode name) can be - installed. Subsequent versions must be upgraded instead. - - Given a fabric peer and orderer - When version one of a chaincode is installed via the CLI - Then installing version two of the same chaincode via the CLI will fail - - Examples: - | lang | - | go | - | java | diff --git a/peer/chaincode/features/instantiate.feature b/peer/chaincode/features/instantiate.feature deleted file mode 100644 index ac110cacd0a..00000000000 --- a/peer/chaincode/features/instantiate.feature +++ /dev/null @@ -1,12 +0,0 @@ -Feature: Instantiate chaincode via CLI - -Scenario Outline: Install & instantiate a first version chaincode via CLI - - Given a fabric peer and orderer - When a chaincode is installed via the CLI - Then the chaincode can be instantiated via the CLI - - Examples: - | lang | - | go | - | java | diff --git a/peer/chaincode/features/steps/steps.py b/peer/chaincode/features/steps/steps.py deleted file mode 100644 index 9eb16b0e664..00000000000 --- a/peer/chaincode/features/steps/steps.py +++ /dev/null @@ -1,126 +0,0 @@ -from behave import * -import os -import random -import subprocess -import tempfile -import time -import socket - -@step(u'a fabric peer and orderer') -def step_impl(context): - - # create network - context.network_name = 'behave_' + ''.join(random.choice('0123456789') for i in xrange(7)) - context.network_id = subprocess.check_output([ - 'docker', 'network', 'create', context.network_name - ]).strip() - - # start orderer - context.orderer_container_id = subprocess.check_output([ - 'docker', 'run', '-d', '-p', '7050', - '--expose', '7050', - '--network', context.network_name, - '--network-alias', 'orderer', - '--env', 'ORDERER_GENERAL_LISTENADDRESS=0.0.0.0', - 'hyperledger/fabric-orderer' - ]).strip() - context.orderer_address = subprocess.check_output(['docker', 'port', context.orderer_container_id, '7050']).strip() - - # start peer - context.peer_container_id = subprocess.check_output([ - 'docker', 'run', '-d', '-p', '7051', - '--network', context.network_name, - '--network-alias', 'vp0', - '--env', 'CORE_PEER_ADDRESSAUTODETECT=true', - '--env', 'CORE_PEER_ID=vp0', - '--env', 'CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer:7050', - '--env', 'CORE_CHAINCODE_STARTUPTIMEOUT=5000', - '--volume', '/var/run/docker.sock:/var/run/docker.sock', - 'hyperledger/fabric-peer', - 'peer', 'node', 'start', '--logging-level', 'debug' - ]).strip() - context.peer_address = subprocess.check_output(['docker', 'port', context.peer_container_id, '7051']).strip() - time.sleep(1) - - # setup env for peer cli commands - context.peer_env = os.environ.copy() - context.peer_env['CORE_PEER_ADDRESS'] = context.peer_address - context.peer_env['CORE_PEER_COMMITTER_LEDGER_ORDERER'] = context.orderer_address - -@step(r'a (?Pjava|go|golang|car) chaincode is installed via the CLI') -def step_impl(context, lang): - context.chaincode_lang = 'golang' if lang == 'go' else lang - context.chaincode_id_name = lang + '_cc_' + ''.join(random.choice('0123456789') for i in xrange(7)) - context.chaincode_id_version = '1.0.0.0' - try: - print(subprocess.check_output([ - context.peer_exe, 'chaincode', 'install', - '--logging-level', 'debug', - '--name', context.chaincode_id_name, - '--path', context.sample_chaincode_path[context.chaincode_lang], - '--version', context.chaincode_id_version, - '--lang', context.chaincode_lang - ], cwd=context.fabric_dir, stderr=subprocess.STDOUT, env=context.peer_env)) - except subprocess.CalledProcessError as e: - print(e.output) - print('CORE_PEER_ADDRESS = ' + context.peer_env['CORE_PEER_ADDRESS']) - print('CORE_PEER_COMMITTER_LEDGER_ORDERER = ' + context.peer_env['CORE_PEER_COMMITTER_LEDGER_ORDERER']) - raise - -@step(u'the chaincode is installed on the peer') -def step_impl(context): - print(subprocess.check_output([ - 'docker', 'exec', context.peer_container_id, 'ls', '-l', '/var/hyperledger/production/chaincodes/' + context.chaincode_id_name + '.' + context.chaincode_id_version - ])) - -@step(r'version (?P\S+) of a (?Pjava|go|golang|car) chaincode is installed via the CLI') -def step_impl(context, version, lang): - context.chaincode_lang = 'golang' if lang == 'go' else lang - context.chaincode_id_name = lang + '_cc_' + ''.join(random.choice('0123456789') for i in xrange(7)) - context.chaincode_id_version = version - try: - print(subprocess.check_output([ - context.peer_exe, 'chaincode', 'install', - '--logging-level', 'debug', - '--name', context.chaincode_id_name, - '--path', context.sample_chaincode_path[context.chaincode_lang], - '--version', context.chaincode_id_version, - '--lang', context.chaincode_lang - ], cwd=context.fabric_dir, stderr=subprocess.STDOUT, env=context.peer_env)) - except subprocess.CalledProcessError as e: - print(e.output) - raise - -@step(r'installing version (?P\S+) of the same chaincode via the CLI will fail') -def step_impl(context, version): - assert getattr(context, 'chaincode_id_name', None), 'No chaincode previously installed.' - context.chaincode_id_version = version - try: - print(subprocess.check_output([ - context.peer_exe, 'chaincode', 'install', - '--logging-level', 'debug', - '--name', context.chaincode_id_name, - '--path', context.sample_chaincode_path[context.chaincode_lang], - '--version', context.chaincode_id_version, - '--lang', context.chaincode_lang - ], cwd=context.fabric_dir, stderr=subprocess.STDOUT, env=context.peer_env)) - except subprocess.CalledProcessError as e: - print(e.output) - raise - -@step(u'the chaincode can be instantiated via the CLI') -def step_impl(context): - assert getattr(context, 'chaincode_id_name', None), 'No chaincode previously installed.' - try: - print(subprocess.check_output([ - context.peer_exe, 'chaincode', 'instantiate', - '--logging-level', 'debug', - '--name', context.chaincode_id_name, - '--path', context.sample_chaincode_path[context.chaincode_lang], - '--version', context.chaincode_id_version, - '--lang', context.chaincode_lang, - '--ctor', context.sample_chaincode_ctor_args[context.chaincode_lang] - ], cwd=context.fabric_dir, stderr=subprocess.STDOUT, env=context.peer_env)) - except subprocess.CalledProcessError as e: - print(e.output) - raise