Skip to main content

Recommended

Create a Hyperledger Fabric network and perform chaincode transactions out of the box!

Write your first chaincode and Fabric client with Chaincoder! You do not need any other IDE or tool, just Java (JRE) and Docker (and as an option the JDK for the client). It is built for Hyperledger Fabric developers as a starting point with the most important features and contains Internet links to discussions and official documentation sites. Chaincoder aims to be an interactive tool for rapid development, creation and deployment of applications on Hyperledger Fabric.


Features

It helps you with the correct installation as it automatically performs the downloads of docker images and fabric binaries and checks installation requirements like docker. Chaincoder currently supports a Java SDK client and the CLI which can be used to interact with a private blockchain in Hyperledger Fabric. Chaincode itself can be written in Go. It uses the "dev mode" for faster chaincode / smart contract compilation.

Network Setup

It comes with a unusual network setup, having 3 organisations, which are connected to each other as pairs on 2 channels like a triangle. SOLO is used as orderer and an OR endorsement policy is applied (at this time CLI only supports OR for queries). For a production release you will need a different setup with Kafka for the orderer and a different membership service provider.


About

This is the first version of the new IDE for Hyperledger Fabric development. I wanted to make a network with chaincode ready to use with the SDK so that you can get a basic idea of how things work in Hyperledger Fabric regarding chaincode and client development. So I spent several weeks on this. The IDE is written in Java and contains the most important artifacts of a fabric project for fast development. I tried to use default Fabric commands only such as CLI commands. It is not perfect, but I released this version, because I believe it might help someone to get started with Fabric.

Donate

If you like it, buy me a pizza :)

If you experience problems or have questions or comments, please write me an email to bernd.noetscher@yahoo.de. Thanks in advance for your support!

Requirements

Java JRE 1.8
Docker 1.8
(if you want to use the SDK, you will need to install the JDK 1.8)

Roadmap

Node.js will follow for chaincode and SDK
Monitor for nodes feature


THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY!
USE AT YOUR OWN RISK!

Announcements

  • 2018/05/29

    Chaincoder v0.2 is now available!

    This is the first version of the new IDE for Hyperledger Fabric development. I wanted to make a network with chaincode ready to use with the SDK so that you can get a basic idea of how things work in Hyperledger Fabric regarding chaincode and client development. So I spent several weeks on this. The IDE is written in Java and contains the most important artifacts of a fabric project for fast development. I tried to use default Fabric commands only such as CLI commands. It is not perfect, but I released this version, because I believe it might help someone to get started with Fabric.

    Download (tested on Mac only)


  • 2018/05/29

    Chaincode Go Example

    package main import ( "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" ) // SimpleAsset implements a simple chaincode to manage an asset type SimpleAsset struct { } // Init is called during chaincode instantiation to initialize any // data. Note that chaincode upgrade also calls this function to reset // or to migrate data. func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { // Get the args from the transaction proposal args := stub.GetStringArgs() if len(args) != 2 { return shim.Error("Incorrect arguments. Expecting a key and a value") } // Set up any variables or assets here by calling stub.PutState() // We store the key and the value on the ledger err := stub.PutState(args[0], []byte(args[1])) if err != nil { return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) } return shim.Success(nil) } // Invoke is called per transaction on the chaincode. Each transaction is // either a 'get' or a 'set' on the asset created by Init function. The Set // method may create a new asset by specifying a new key-value pair. func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { // Extract the function and args from the transaction proposal fn, args := stub.GetFunctionAndParameters() var result string var err error if fn == "set" { result, err = set(stub, args) } else { // assume 'get' even if fn is nil result, err = get(stub, args) result = result + "!!#well done#!!" } if err != nil { return shim.Error(err.Error()) } // Return the result as success payload return shim.Success([]byte(result)) } // Set stores the asset (both key and value) on the ledger. If the key exists, // it will override the value with the new one func set(stub shim.ChaincodeStubInterface, args []string) (string, error) { if len(args) != 2 { return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value") } err := stub.PutState(args[0], []byte(args[1])) if err != nil { return "", fmt.Errorf("Failed to set asset: %s", args[0]) } return args[1], nil } // Get returns the value of the specified asset key func get(stub shim.ChaincodeStubInterface, args []string) (string, error) { if len(args) != 1 { return "", fmt.Errorf("Incorrect arguments. Expecting a key") } value, err := stub.GetState(args[0]) if err != nil { return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err) } if value == nil { return "", fmt.Errorf("Asset not found: %s", args[0]) } return string(value), nil } // main function starts up the chaincode in the container during instantiate func main() { if err := shim.Start(new(SimpleAsset)); err != nil { fmt.Printf("Error starting SimpleAsset chaincode: %s", err) } }




  •  

    SDK Java Fabric Client Example

    /* Simple Java Client for Hyperlegder Fabric - sets and gets a value from the Ledger */ import java.io.*; import java.security.*; import java.security.spec.*; import java.util.*; import java.util.concurrent.*; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.hyperledger.fabric.sdk.*; import org.hyperledger.fabric.sdk.exception.*; import org.hyperledger.fabric.sdk.security.*; public class JavaSDKClient { final static String PATH_CRYPTO_CONFIG = "$$$PATH_CRYPTO_CONFIG$$$"; // change this line to the correct path! public static void main(String[] args) { try { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); HFClient client = getClient(); Channel channel = getChannel(client); query(client, channel); invoke(client, channel); query(client, channel); } catch (Exception ex) { System.out.println(ex.toString()); } } static void query(HFClient client, Channel channel) throws ProposalException, InvalidArgumentException { QueryByChaincodeRequest qpr = client.newQueryProposalRequest(); ChaincodeID id = ChaincodeID.newBuilder().setName("chaincode1").build(); qpr.setChaincodeID(id); qpr.setFcn("query"); qpr.setArgs(new String[]{"a"}); Collection res = channel.queryByChaincode(qpr); for (ProposalResponse pres : res) { String s = new String(pres.getChaincodeActionResponsePayload()); System.out.println(s); } } static void invoke(HFClient client, Channel channel) throws InvalidArgumentException, ProposalException, ChaincodeEndorsementPolicyParseException, IOException { TransactionProposalRequest req = client.newTransactionProposalRequest(); ChaincodeID cid = ChaincodeID.newBuilder().setName("chaincode1").build(); req.setChaincodeID(cid); req.setFcn("set"); req.setArgs(new String[]{"a", "77"}); final Collection responses = channel.sendTransactionProposal(req, channel.getPeers()); CompletableFuture txFuture = channel.sendTransaction(responses, client.getUserContext()); BlockEvent.TransactionEvent event; try { event = txFuture.get(); System.out.println(event.toString()); } catch (InterruptedException ex) { System.out.println(ex.toString()); } catch (ExecutionException ex) { System.out.println(ex.toString()); } } static HFClient getClient() { HFClient client = null; try { client = getHfClient(); try { client.setUserContext(new User() { public String getName() { return "PeerAdmin"; } public Set getRoles() { return null; } public String getAccount() { return null; } public String getAffiliation() { return null; } public Enrollment getEnrollment() { return new Enrollment() { public PrivateKey getKey() { // load admin private key PrivateKey privateKey = null; try { String k = validFile(PATH_CRYPTO_CONFIG + "/peerOrganizations/org1.chaincoder.org/users/Admin@org1.chaincoder.org/msp/keystore"); File privateKeyFile = findFileSk(k); privateKey = getPrivateKeyFromBytes(toByteArray(new FileInputStream(privateKeyFile))); } catch (FileNotFoundException ex) { System.out.println(ex.toString()); } catch (IOException ex) { System.out.println(ex.toString()); } catch (Exception ex) { System.out.println(ex.toString()); } return privateKey; } public String getCert() {// read admin client certificate String certificate = null; try { String k = validFile(PATH_CRYPTO_CONFIG + "/peerOrganizations/org1.chaincoder.org/users/Admin@org1.chaincoder.org/msp/signcerts/Admin@org1.chaincoder.org-cert.pem"); File certificateFile = new File(k); certificate = new String(toByteArray(new FileInputStream(certificateFile)), "UTF-8"); } catch (UnsupportedEncodingException ex) { System.out.println(ex.toString()); } catch (FileNotFoundException ex) { System.out.println(ex.toString()); } catch (IOException ex) { System.out.println(ex.toString()); } return certificate; } }; } public String getMspId() { return "Org1MSP"; } }); } catch (InvalidArgumentException ex) { System.out.println(ex.toString()); } } catch (Exception ex) { System.out.println(ex.toString()); } return client; } static Channel getChannel(HFClient client) throws InvalidArgumentException, TransactionException, ProposalException { Channel channel = client.newChannel("channel1x2"); Properties ordererProperties = new Properties(); ordererProperties.setProperty("pemFile", validFile(PATH_CRYPTO_CONFIG + "/ordererOrganizations/chaincoder.org/orderers/orderer.chaincoder.org/tls/server.crt")); ordererProperties.setProperty("trustServerCertificate", "true"); // testing // environment // only // NOT // FOR // PRODUCTION! ordererProperties.setProperty("hostnameOverride", "orderer.chaincoder.org"); ordererProperties.setProperty("sslProvider", "openSSL"); ordererProperties.setProperty("negotiationType", "TLS"); ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[]{5L, TimeUnit.MINUTES}); ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[]{8L, TimeUnit.SECONDS}); channel.addOrderer(client.newOrderer("orderer.chaincoder.org", "grpcs://localhost:7050", ordererProperties)); // use the network orderer container URL Properties peerProperties; peerProperties = new Properties(); peerProperties.setProperty("pemFile", validFile(PATH_CRYPTO_CONFIG + "/peerOrganizations/org1.chaincoder.org/peers/peer0.org1.chaincoder.org/tls/server.crt")); peerProperties.setProperty("trustServerCertificate", "true"); // testing peerProperties.setProperty("hostnameOverride", "peer0.org1.chaincoder.org"); peerProperties.setProperty("sslProvider", "openSSL"); peerProperties.setProperty("negotiationType", "TLS"); peerProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000); channel.addPeer(client.newPeer("peer0.org1.chaincoder.org", "grpcs://127.0.0.1:7051", peerProperties)); channel.initialize(); return channel; } static HFClient getHfClient() throws Exception { CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite(); HFClient client = HFClient.createNewInstance(); client.setCryptoSuite(cryptoSuite); return client; } // ******************************************************************************* // ************************* helper functions ************************************ // ******************************************************************************* static PrivateKey getPrivateKeyFromBytes(byte[] data) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { final Reader pemReader = new StringReader(new String(data)); final PrivateKeyInfo pemPair; PEMParser pemParser = new PEMParser(pemReader); pemPair = (PrivateKeyInfo) pemParser.readObject(); PrivateKey privateKey = new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getPrivateKey(pemPair); return privateKey; } static File findFileSk(String directorys) { File directory = new File(directorys); File[] matches; matches = directory.listFiles(); if (null == matches) { throw new RuntimeException(java.lang.String.format("Matches returned null does %s directory exist?", directory.getAbsoluteFile().getName())); } if (matches.length != 1) { throw new RuntimeException(java.lang.String.format("Expected in %s only 1 sk file but found %d", directory.getAbsoluteFile().getName(), matches.length)); } return matches[0]; } static byte[] toByteArray(InputStream input) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int nRead; byte[] data = new byte[16384]; while ((nRead = input.read(data, 0, data.length)) != -1) { buffer.write(data, 0, nRead); } buffer.flush(); return buffer.toByteArray(); } static String validFile(String s) { if (directoryExists(s) || fileExists(s)) { } else { System.out.println(s + " not exsting!!"); } return s; } static boolean directoryExists(String filePathString) { File f = new File(filePathString); if (f.exists() && f.isDirectory()) { return true; } return false; } static boolean fileExists(String filePathString) { File f = new File(filePathString); if (f.exists() && !f.isDirectory()) { return true; } return false; } }

Back to the top