This tutorial shows you how to use.NET Core to develop a simple Hyperledger Fabric chain code for basic asset management and transfer operations.If you're a skilled.NET/C# developer and need to use Hyperledger Fabric as a block chain platform for a variety of reasons, this is an alternative to turning around and throwing yourself into Java/Node.js/Go.
Recommendations for related tutorials:
1. Fabric Chain Code.NET Core Development Package
Start by creating a.NET Core project that adds a dependency on the Fabric Chain Code.NET Core Development Package by executing the following command from the command line:
dotnet add package Thinktecture.HyperledgerFabric.Chaincode \ --version 1.0.0-prerelease.74
The Thinktecture.HyperledgerFabric.Chaincode chain code development package provides two different ways to develop Hyperledger Fabric chain codes.
- Using the underlying API: Developing Super Account Fabric Chain Codes by implementing the IChaincode interface is a way to compare the underlying. The advantage is maximum control over chain code implementation
- Using the upper API: It is easier to develop a super-ledger Fabric chain code by inheriting the ContractBase class, but The price is a partial loss of flexibility
In this tutorial, we will implement Fabric chain codes through the IChaincode interface, a bottom-level approach.If you are more interested in implementing ontractBase inheritance, please wait for our next tutorial!
2. Development of Hyperledger Fabric chain code
Create a class AssetHolding to implement the IChaincode interface:
public class AssetHolding : IChaincode { public async Task<Response> Init(IChaincodeStub stub) { throw new NotImplementedException(); } public Task<Response> Invoke(IChaincodeStub stub) { throw new NotImplementedException(); } }
Both methods of the Ichaincode interface, Init and Invoke, need to be implemented.
The Init() method is called when the Fabric chain code is initialized and upgraded and can be used to initialize the asset library, for example, Set some defaults.
The Invoke() method is called by the Peer node throughout the life cycle of the Fabric chain code and can be used to handle business transaction logic that may affect the state of the asset.
Both methods have a parameter of type IChaincodeStub that encapsulates the communication API between the Fabric chain code implementation and the Fabric peer node.For example, this interface allows CRUD operations on asset libraries.
3. Init() Method for Implementing Hyperledger Fabric Chain Code
As mentioned earlier, our Fabric chain code needs to initialize two account names and set the initial balance of the account.Basically it is used to pass an array parameter to the chain code when calling Fabric chain code, such as ['accountA','100','accountB','50']. In order to get this parameter when Fabric chain code is initialized, we can use stub.GetFunctionAndParameters(), which results in a parameter object of type List <String>, which contains all the parameters.
public async Task<Response> Init(IChaincodeStub stub) { var functionAndParameters = stub.GetFunctionAndParameters(); var args = functionAndParameters.Parameters; args.AssertCount(4); }
We use Parameters.AssertCount(4) to quickly check if the number of parameters is up to par, and throw an exception if the number is not 4, terminating chain code execution.
The next step is to convert two of these parameters to integers.We can do this on our own using int.TryParse(), or using the args.TryGet<int>() method.Now let's complete the implementation of Init():
public async Task<Response> Init(IChaincodeStub stub) { var functionAndParameters = stub.GetFunctionAndParameters(); var args = functionAndParameters.Parameters; args.AssertCount(4); if (!args.TryGet<int>(1, out var aValue) || !args.TryGet<int>(3, out var bValue)) { return Shim.Error("Expecting integer value for asset holding"); } }
In the code above, we attempted to convert the second and fourth parameters to integers, and if a conversion failed, we returned Shim.Error() to notify the caller of the Fabric chain code that initialization failed.
If the conversion is successful, we can use stub.PutState() to store the conversion results in the chain code asset library:
public async Task<Response> Init(IChaincodeStub stub) { var functionAndParameters = stub.GetFunctionAndParameters(); var args = functionAndParameters.Parameters; args.AssertCount(4); if (!args.TryGet<int>(1, out var aValue) || !args.TryGet<int>(3, out var bValue)) { return Shim.Error("Expecting integer value for asset holding"); } if (await stub.PutState(args.Get<string>(0), aValue) && await stub.PutState(args.Get<string>(2), bValue)) { return Shim.Success(); } return Shim.Error("Error during Chaincode init!"); }
In the code above, we use PutState() to update the initial value of the account in the Faric Chain Code Asset Library.If everything goes well, we can use Shim.Success() to return a successful response to the Fabric chain code caller, otherwise an error is returned.
4. Invoke() Method for Implementing Hyperledger Fabric Chain Code
Now let's move on to the implementation of the Invoke method.The Invoke() method is called by Fabric's peer nodes throughout the chain code declaration cycle to process business logic. It has the same parameters as Init(), so we also need to use the GetFunctionAndParameters() method of the parameter interface to get the call parameters of the Fabric chain code.
public Task<Response> Invoke(IChaincodeStub stub) { var functionAndParameters = stub.GetFunctionAndParamaters(); if (functionAndParameters.Function == 'myfn1') { return MyFn1(stub, functionAndParameters.Parameters); } if (functionAndParameters.Function == 'myfn2') { return MyFn2(stub, functionAndParameters.Parameters); } // Rinse and repeat for every function }
Depending on your design, many if or switch branches may be required in the Invoke implementation, so the Faric Chain Code.NET development kit provides an auxiliary class, ChaincodeInvocationMap, to make your code cleaner:
private readonly ChaincodeInvocationMap _invocationMap; public AssetHolding() { _invocationMap = new ChaincodeInvocationMap { {"Transfer", InternalTransfer}, {"Query", InternalQuery} }; } public Task<Response> Invoke(IChaincodeStub stub) { return _invocationMap.Invoke(stub); }
It should be noted that we have not yet implemented the InternalTransfer() and InternalQuery() methods.
The following implements the InternalTransfer() method:
private async Task<ByteString> InternalTransfer(IChaincodeStub stub, Parameters args) { args.AssertCount(3); var accountA = args.Get<string>(0); var accountB = args.Get<string>(1); if (string.IsNullOrEmpty(accountA) || string.IsNullOrEmpty(accountB)) throw new Exception("Asset holding must not be empty"); var aValue = await stub.TryGetState<int>(accountA); if (!aValue.HasValue) throw new Exception("Failed to get state of asset holder A"); var bValue = await stub.TryGetState<int>(accountB); if (!bValue.HasValue) throw new Exception("Failed to get state of asset holder B"); if (!args.TryGet<int>(2, out var amount)) throw new Exception("Expecting integer value for amount to be transferred"); aValue -= amount; bValue += amount; await stub.PutState(accountA, aValue); await stub.PutState(accountB, bValue); return ByteString.Empty; }
Since our Fabric chain code requires transferring money from one account to another, three parameters are required:
- accountA: Transfer account
- accountB: Transfer to account
- Amount: transfer amount
We can use AssertCount(3) to ensure that we get three parameters, just like the checks in the Init() implementation.Then, before we transfer, we need to read the current account status from the Fabric chain code asset library.To do this, we need to use either the IChaincodeStub.GetState() method or the IChaincodeStub.TryGetState() method, which differs in that the second method does not throw an exception and only returns false if it fails.
After reading the account status from the Fabric chain code asset library, we can deduct the transfer amount from accountA and add it to accountB.At this stage, you can check if necessary, such as if the transfer amount is not negative.
After updating the status variables for both accounts, we also need to write the new status to the asset library using stub.PutState().Finally, we return an empty ByteString to the Fabric chain code caller to indicate that no error occurred.
The InternalQuery() method is used to query the balance of a specified account, so you need to pass in a parameter indicating the account to query.
private async Task<ByteString> InternalQuery(IChaincodeStub stub, Parameters args) { args.AssertCount(1); var a = args[0]; var aValueBytes = await stub.GetState(a); if (aValueBytes == null) throw new Exception($"Failed to get state of asset holder {a}"); return aValueBytes; }
Okay, now we've finished the.NET/C#implementation of the Fabric chain code, but don't forget to implement the Maine() method as the entry point for.NET applications, where we need to start the chain code:
static async Task Main(string[] args) { using (var provider = ChaincodeProviderConfiguration.Configure<AssetHolding>(args)) { var shim = provider.GetRequiredService<Shim>(); await shim.Start(); } }
Original Link: Developing Hyperledger Fabric Chain Codes with.NET/C#