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 Docker (and Java as an option for client development). 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. With this tool you can freely use Hyperledger Fabric locally or with your own cloud setup without the need to pay fees to third parties.


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, Node.js or Java. It uses the "dev mode" for faster Go chaincode (smart contract) compilation.

Network Setup

Additionally, Chaincoder lets you define any network setup required. It comes with several examples, one 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 or AND endorsement policy is applied. Changes in configuration can be easily done. For a production release you will need a different setup with Kafka (or other) for the orderer and a different membership service provider.


Download



   Download Chaincoder V1.01 Beta as zip file (macOS)



About

This are the first versions 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 the latest 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@chaincoder.org. Thanks in advance for your support!

Requirements

Docker 1.8
(if you want to use the Java SDK for client development, you will need to install the JDK 11)

Links



   GitHub - Source Codes, Issues, ...


Roadmap

Go and Node.js will follow for client development



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

Announcements

  • 2019/09/19

    Chaincoder V1.01 Beta for Windows and Mac is now available!

    NEW
    + Bug fixes



  • 2018/12/13

    Chaincoder V1.0 Beta for Windows is now available!

    NEW
    + Chaincode development with Golang, Java and Node.js

    + Examples for Golang, Java and Node.js Chaincode development

    + Java client with AND policy support

    + CLI client with AND policy support

    + CLI client script support to call chaincode functions to test your distributed blockchain based smart contract

    + View the blockchain information on a specific peer in a Hyperledger Fabric network and read how many blocks were created and how many transactions were performed

    + Add minor changes in GUI and more help

    + Switch to Java 11 and JavaFX



  • 2018/12/13

    Chaincoder V1.0 Beta for macOS is now available!

    NEW
    + Chaincode development with Golang, Java and Node.js

    + Examples for Golang, Java and Node.js Chaincode development

    + Java client with AND policy support

    + CLI client with AND policy support

    + CLI client script support to call chaincode functions to test your distributed blockchain based smart contract

    + View the blockchain information on a specific peer in a Hyperledger Fabric network and read how many blocks were created and how many transactions were performed

    + Add minor changes in GUI and more help

    + Switch to Java 11 and JavaFX



  • 2018/12/13

    Chaincoder v0.41 for Mac

    NEW
    + New project and open project feature
    FIX
    + Display bug in rare cases when using editor not showing any source codes



  • 2018/12/9

    Chaincoder v0.4 for Windows is now available!

    NEW

    Note: You will need Windows 10 Pro or Enterprise editor, because of the Docker installation requirements.



  • 2018/10/12

    Chaincoder v0.4 is now available!

    NEW: Multi-node blockchain network for production environment

    Chaincoder, unlike many others, is able to manage multiple independent hosts running in a cloud environment or own network for each organization (peers). It is recommended to use such a setting for production purposes.
    It enables you to write and deploy native without intermediate layer like Hyperledger Composer and its dependencies. Only the pure system with chaincodes, peers, orderers and channels of Hyperledger Fabric are used.
    If you need a different setup than 3 organizations with 1 peer each joining the consortium network or help, just ask for help.
    If you setup your nodes in a cloud like AWS it is important that several ports are accessible to the world. E. g. the security group of AWS should allow inbound TCP connections to following ports: 22, 7050, 7051, and 7053. Needless to say, make sure you are in possession of the private key used to run the cloud instances.
    Note: The official AWS Blockchain Template for Hyperledger Fabric only supports a docker-local container platform at the time of writing, meaning the Hyperledger Fabric containers are deployed on a single EC2 instance only.



  • 2018/05/29

    Chaincoder v0.3 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.





  • 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