Introduction to Nethereum Blockchain in dotNetCore

Author
Casper Broeren
Categories
Publish date

This article describes the process of using a blockchain platform like Ethereum in dotNet core. The target audience are other dotNet developers who want to start with Ethereum. Understanding of blockchain is needed. In this article we construct a full example that allows you to interact with a custom written smart contract.

The first era of blockchain can be viewed as Bitcoin only and no smart contracts. Nevertheless, the second era of blockchain is showing to be more promising. With more blockchain platforms besides Bitcoin, it’s showing more mature and blockchains have more possibilities. The Ethereum blockchain is more a distributed ledger with smart contracts which uses a crypto currency. The focus of Ethereum is more on the smart contract part, then on crypto currency. The purpose of Ether (Ethereum’s crypto currency) is to give pay for executing transactions which can be mining contracts or executing contracts.

A smart contract is a piece of code written for the Ethereum Virtual Machine. This can be written in Solidity and compiled to byte code. This byte code is put in the ledger and becomes immutable but still can be interacted with, and can have changing states. As Ethereum documentation puts it “From a practical standpoint, the EVM can be thought of as a large decentralized computer containing millions of objects, called "accounts", which have the ability to maintain an internal database, execute code and talk to each other.” From a developer standpoint, you can view Solidity as a Javascript like language, which is a bit limited. Since the Solidity code runs in a blockchain there are good reasons for it to be so limited. Something simple as random numbers are a bit of a challenge. Also getting data with a Http call isn’t possible because the truth needs to lie in the system. Still you can call contracts and put in data to change states, so external influence is possible.

First of all install the Mist browser and Geth. The Mist browser is a GUI which acts as a wallet for your Ether. Geth is the program-interface which your code connects to, to which Geth connects to the blockchain of Ethereum. For this article, we will be using the testnet. This way we can mine some Ether for free. When Mist has been started, select using the test net from the menu. Create an account and mine some coins (menu item Develop and start mining)

After some time, you will have some Ether. This comes in handy when doing transactions. Even releasing a contract or executing a contract costs ether.
Now let’s close the wallet otherwise you can’t open a new geth process. So start your installed Geth in a console;

“\Program Files\Geth\geth” --testnet --rpcapi eth,web3,personal --rpc

Above the result of our command. And we see that’s picking up the current blockchain cache and it has the http endpoint listening on localhost:8545. This is important because we, the Mist browser and other applications are going to access this with IPC or RPC. Since the IPC implementation is only supported on Windows, we can’t use this in dotNetCore. We stick to web3 RPC in our solution.

Now you can open the wallet again. Only it’s not possible to start mining because the standalone Geth is running.

Now it’s time to start developing, open Visual Studio and create a new project. Please note that the code is available at our Github. Create a “ASP.NET Core Web Application” and then chose the Web.API template. We’re going to create a service with some methods to interact with the blockchain and release a contract to the blockchain. This coin piggybank contract will store our coin balance. After the contract is mined we can call the methods of contract. Nothing epic and surely not a full application but it’s nice to see what we can do. We have chosen to make the system persistent with Azure Table storage, it’s fast and cheap.

First add these dependencies to your Project.json;

    "Nethereum.Web3": "2.0.0-rc1",
    "Portable.BouncyCastle": "1.8.1.1",
    "WindowsAzure.Storage": "8.1.1"

Save and watch the packages being restored. The first two are for Ethereum the last for table storage. Nethereum.Web3 is the whole library for accessing your local Geth process through RPC json. The BouncyCastle is a crypto library needed for Nethereum.

First we need a model to capture our Ethereum Contract State. Ethereum doesn’t have any options for getting contracts back out of the blockchain, mainly for security/immutable reasons. Once the contract is put into the blockchain it cannot be changed or Solidity code cannot be retrieved. That’s why we need to store this information in our system. Create a file called EthereumContractInfo derived from Azure Storage class TableEntity in your model folder;

using Microsoft.WindowsAzure.Storage.Table;

namespace EthereumStart.Models
{
    public class EthereumContractInfo : TableEntity
    {
        public string Abi { get; set; }
        public string Bytecode { get; set; }
        public string TransactionHash { get; set; }
        public string ContractAddress { get; set; }

        public EthereumContractInfo()
        {

        }

        public EthereumContractInfo(string name, string abi, string bytecode, string transactionHash)
        {
            PartitionKey = "contract";
            RowKey = name;
            Abi = abi;
            Bytecode = bytecode;
            TransactionHash = transactionHash;
        }
    }
}

Now create a folder with the name Services and create file IEthereumService the interface so we can use it for Dependency Injection;

using System.Threading.Tasks;
using EthereumStart.Models;
using Nethereum.Contracts;

namespace EthereumStart.Services
{
    public interface IEthereumService
    {
        string AccountAddress { get; set; }
        Task<bool> SaveContractToTableStorage(EthereumContractInfo contract);
        Task<EthereumContractInfo> GetContractFromTableStorage(string name);
        Task<decimal> GetBalance(string address);
        Task<bool> ReleaseContract(string name, string abi, string byteCode, int gas);
        Task<string> TryGetContractAddress(string name);
        Task<Contract> GetContract(string name);
    }
}

All methods should return a task because we want to make the implementation to use async. The idea is that we going to release the contract, try to get it’s address and then invoke it’s methods on that address. Now we create the file BasicEthereumService to implement the interface.

using Microsoft.Extensions.Options;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Table;
using Nethereum.Web3;
using System;
using System.Threading.Tasks;
using EthereumStart.Models;
using Nethereum.Contracts;

namespace EthereumStart.Services
{
    public class BasicEthereumService : IEthereumService
    {
        private Nethereum.Web3.Web3 _web3;
        private string _accountAddress;
        private string _password;
        private string _storageKey;
        private string _storageAccount;

        public string AccountAddress
        {
            get
            {
                return _accountAddress;
            }

            set
            {
                _accountAddress = value;
            }
        }

        public BasicEthereumService(IOptions<EthereumSettings> config)
        {
            _web3 = new Web3("http://localhost:8545");
            _accountAddress = config.Value.EhtereumAccount;
            _password = config.Value.EhtereumPassword;
            _storageAccount = config.Value.StorageAccount;
            _storageKey = config.Value.StorageKey;
        }

        public async Task<bool> SaveContractToTableStorage(EthereumContractInfo contract)
        {
            StorageCredentials credentials = new StorageCredentials(_storageAccount, _storageKey);
            CloudStorageAccount account = new CloudStorageAccount(credentials, true);
            var client = account.CreateCloudTableClient();

            var tableRef = client.GetTableReference("ethtransactions");
            await tableRef.CreateIfNotExistsAsync();

            TableOperation ops = TableOperation.InsertOrMerge(contract);
            await tableRef.ExecuteAsync(ops);
            return true;
        }

        public async Task<EthereumContractInfo> GetContractFromTableStorage(string name)
        {
            StorageCredentials credentials = new StorageCredentials(_storageAccount, _storageKey);
            CloudStorageAccount account = new CloudStorageAccount(credentials, true);
            var client = account.CreateCloudTableClient();

            var tableRef = client.GetTableReference("ethtransactions");
            await tableRef.CreateIfNotExistsAsync();

            TableOperation ops = TableOperation.Retrieve<EthereumContractInfo>("contract", name);
            var tableResult = await tableRef.ExecuteAsync(ops);
            if (tableResult.HttpStatusCode == 200)
                return (EthereumContractInfo)tableResult.Result;
            else
                return null;
        }

        public async Task<decimal> GetBalance(string address)
        {
            var balance = await _web3.Eth.GetBalance.SendRequestAsync(address);
            return _web3.Convert.FromWei(balance.Value, 18);
        }

        public async Task<bool> ReleaseContract(string name, string abi, string byteCode, int gas)
        {

            // check contractName
            var existing = await this.GetContractFromTableStorage(name);
            if (existing != null) throw new Exception($"Contract {name} is present in storage");
            try
            {
                var resultUnlocking = await _web3.Personal.UnlockAccount.SendRequestAsync(_accountAddress, _password, 60);
                if (resultUnlocking)
                {
                    var transactionHash = await _web3.Eth.DeployContract.SendRequestAsync(abi, byteCode, _accountAddress, new Nethereum.Hex.HexTypes.HexBigInteger(gas), 2);

                    EthereumContractInfo eci = new EthereumContractInfo(name, abi, byteCode, transactionHash);
                    return await SaveContractToTableStorage(eci);
                }
            }
            catch (Exception exc)
            {
                return false;
            }
            return false;
        }

        public async Task<string> TryGetContractAddress(string name)
        {
            // check contractName
            var existing = await this.GetContractFromTableStorage(name);
            if (existing == null) throw new Exception($"Contract {name} does not exist in storage");

            if (!String.IsNullOrEmpty(existing.ContractAddress))
                return existing.ContractAddress;
            else
            {
                var resultUnlocking = await _web3.Personal.UnlockAccount.SendRequestAsync(_accountAddress, _password, 60);
                if (resultUnlocking)
                {
                    var receipt = await _web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(existing.TransactionHash);
                    if (receipt != null)
                    {
                        existing.ContractAddress = receipt.ContractAddress;
                        await SaveContractToTableStorage(existing);
                        return existing.ContractAddress;
                    }
                }
            }
            return null;
        }

        public async Task<Contract> GetContract(string name)
        {
            var existing = await this.GetContractFromTableStorage(name);
            if (existing == null) throw new Exception($"Contract {name} does not exist in storage");
            if (existing.ContractAddress == null) throw new Exception($"Contract address for {name} is empty. Please call TryGetContractAddress until it returns the address");

            var resultUnlocking = await _web3.Personal.UnlockAccount.SendRequestAsync(_accountAddress, _password, 60);
            if (resultUnlocking)
            {
                return _web3.Eth.GetContract(existing.Abi, existing.ContractAddress);
            }
            return null;
        }
    }
}

That is a lot of code. I’m going to skip over the Save- and Load -ContractFromTableStorage because those are just simple Azure table interactions.

In the constructor we see the connection to our Geth process, we connect to port 8545 so it can do RPC json communication.

First method is; GetBalance. Since everything revolves around money, this is nice to check the ether balance of an address like your account, wallet or even a contract. All Ethereum interaction is done through the object web3 in this example. After we got our balance back in Wei, this is like the cent to a euro but then 10^18 factor instead of 10^2. We can convert it back to Ether with the convert.FromWEi

Second method implementation is ReleaseContract. It first checks if we didn’t already released the contract and persist it in storage. If not we can start unlocking the account for 120 seconds. Unlocking is needed when we want to deploy a contract or something else. After that we can call the deploy method and get the transaction hash back. This is needed because now the contract will be mined. Think of mining as the process that the peers of your blockchain do, so that the contract gets accepted into the blockchain. When 12 peers have done so, the contract address is given back. This mining process cost money (aka Gas money) and will be deducted from the _accountAddress you have put in. This amount is in Wei and we specify this in our controller which will call the EthereumService. Each contract has a different gas price. This value is available when compiling the contract. We can specify the contract constructor parameter in the method SendRequestAsync. In our case we specify 2, because our balance should be 2 coins when the contract is released.

As explained the deploy must be mined to get the contract address. We need this address to call the methods on it. In our TryGetContractAddress we check if our contract has got an address already in our table storage and if not we ask the Ethereum Blockchain. If the GetTransactionReceipt returns a valid address, we can persist it.

The last method of our service is the GetContract and this just getting a reference to the Ethereum contract. As you can see the contract must exist in the table storage in order to get the contract address. We’ll be covering calling contracts after the next part.

So now we do a step back from dotNet and go to the solidity program language. First let’s view our test solidity contract;

pragma solidity ^0.4.6;
contract CoinsContract  {
    uint public balance;
    function CoinsContract(uint initial) {
        balance = initial;
    }
    function addCoins(uint add) returns (uint b) {
       b = balance + add;
       return b;
    }
    function subtractCoins(uint add) returns (uint b) {
       b = balance - add;
       return b;
    }
}

It’s just a piggybank which based on its constructor value starts with that balance. Then we can call the add and subtract to modify our coin balance. I know this is very basic but that’s always nice for a start, right? When the contract is released we can call addCoints or subtractCoints method from our dotNet code. So why you want to do that.. it will only cost us ether? Well the upside is that every call to a method will be added to the distributive ledger and so can be viewed at https://testnet.etherscan.io/

In order to release this contract, we need to compile it to byte code. We use the Remix website https://ethereum.github.io/browser-solidity/ This rudimentary web-based editor you can compile and test your contract. Once compiled we can get the Byte code (please don’t forget the 0x in front of it) and the Interface which is also called ABI. Both parts need to be supplied when releasing a contract. The ABI stands Application Binary Interface and is like the WSDL of a webservice.

Back to Visual Studio and we only have to do four more steps before we can release the contracts and start calling the methods. First, we create the settings file called EthereumSettings

namespace EthereumStart.Model
{
    public class EthereumSettings
    {
        public EthereumSettings()
        {
        }
        public string EhtereumAccount { get; set; }
        public string EhtereumPassword { get; set; }
        public string StorageKey { get; set; }
        public string StorageAccount { get; set; }

    }
}

Second we add these settings to the appsettings.json;

 "ehtereumAccount": "x",
 "ehtereumPassword": "y",
 "storageKey": "w",
 "storageAccount": "v"

Of course, not with these values but with your own Ethereum account and password and with your Azure storage account and key. Third we add to our startup.cs the code below in the ConfigureServices method;

 services.Configure<EthereumSettings>(Configuration);
 services.AddScoped<IEthereumService, BasicEthereumService>();

For our last step, we add a controller with the name EthereumTestController and the contents should be;

using EthereumStart.Services;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;

namespace EthereumStart.Controllers
{
    [Route("api/[controller]")]
    public class EthereumTestController : Controller
    {
        private IEthereumService service;
        private const string abi = @"[{""constant"":false,""inputs"":[{""name"":""add"",""type"":""uint256""}],""name"":""addCoins"",""outputs"":[{""name"":""b"",""type"":""uint256""}],""payable"":false,""type"":""function""},{""constant"":false,""inputs"":[{""name"":""add"",""type"":""uint256""}],""name"":""subtractCoins"",""outputs"":[{""name"":""b"",""type"":""uint256""}],""payable"":false,""type"":""function""},{""constant"":true,""inputs"":[],""name"":""balance"",""outputs"":[{""name"":"""",""type"":""uint256""}],""payable"":false,""type"":""function""},{""inputs"":[{""name"":""initial"",""type"":""uint256""}],""payable"":false,""type"":""constructor""}]";
        private const string byteCode = "0x6060604052341561000c57fe5b604051602080610185833981016040528080519060200190919050505b806000819055505b505b610143806100426000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630173e3f41461005157806349fb396614610085578063b69ef8a8146100b9575bfe5b341561005957fe5b61006f60048080359060200190919050506100df565b6040518082815260200191505060405180910390f35b341561008d57fe5b6100a360048080359060200190919050506100f8565b6040518082815260200191505060405180910390f35b34156100c157fe5b6100c9610111565b6040518082815260200191505060405180910390f35b600081600054019050806000819055508090505b919050565b600081600054039050806000819055508090505b919050565b600054815600a165627a7a723058200085d6d7778b3c30ba2e3bf4af4c4811451f7367109c1a9b44916d876cb67c5c0029";
        private const int gas = 4700000;

        public EthereumTestController(IEthereumService ethereumService)
        {
            service = ethereumService;
        }

        [HttpGet]
        [Route("getBalance/{walletAddress}")]
        public async Task<decimal> GetBalance([FromRoute]string walletAddress)
        {
            return await service.GetBalance(walletAddress);
        }
        [HttpGet]
        [Route("releaseContract/{name}")]
        public async Task<bool> ReleaseContract([FromRoute] string name)
        {
            return await service.ReleaseContract(name, abi, byteCode, gas);
        }

        [HttpGet]
        [Route("checkContract/{name}")]
        public async Task<bool> CheckContract([FromRoute] string name)
        {
            return await service.TryGetContractAddress(name) != null;
        }

        [HttpGet]
        [Route("exeContract/{name}/{contractMethod}/{value}")]
        public async Task<string> ExecuteContract([FromRoute] string name, [FromRoute] string contractMethod, [FromRoute] int value)
        {
            string contractAddress = await service.TryGetContractAddress(name);
            var contract = await service.GetContract(name);
            if (contract == null) throw new System.Exception("Contact not present in storage");
            var method = contract.GetFunction(contractMethod);
            try
            {
                // var result = await method.CallAsync<int>(value);
                var result = await method.SendTransactionAsync(service.AccountAddress, value);
                return result.ToString();
            }
            catch (Exception ex)
            {
                return "error";
            }
        }

        [HttpGet]
        [Route("checkValue/{name}/{functionName}")]
        public async Task<int> CheckValue([FromRoute] string name, [FromRoute] string functionName)
        {
            string contractAddress = await service.TryGetContractAddress(name);
            var contract = await service.GetContract(name);
            if (contract == null) throw new System.Exception("Contact not present in storage");
            var function = contract.GetFunction(functionName);

            var result = await function.CallAsync<int>();

            return result;
        }
    }
}

It looks a lot of code but it’s a few methods; First of all we have the ABI and Byte code of our contract, second in the constructor we load the service. Then we have the 4 http calls we can invoke (please prepend your localhost + port yourself)

/api /EthereumTest /getBalance /0xfC1857DD580B41c03D7 e086dD23e7cB e1f0Edd17
This checks a wallet and should return 5 Ehter

/api /EthereumTest /releaseContract /coins
This releases the contract saves the result to Azure storage.

/api /EthereumTest /checkContract /coins
This checks if the contract address is available. If true the contract address is present and we can invoke it. This maybe take some time (sometimes 2 minutes but sometimes 20 seconds)

/api /EthereumTest /exeContract /coins /addCoins /123
The actual invoking of the contract and the method addCoins with value 123. Once this is called a transaction result is given. It’s possible to use CallAsync but then it would be called in your local Ethereum VM, so this would not result in a transaction. Because it’s a transaction, the transaction address is returned. We can see our contract calls on the Etherscan website as well. Etherscan is showing all transaction for the main and test network of Ethereum. With this you can proof you did a transaction. Here is one of ours

/api /EthereumTest /checkValue /coins /balance
When the transaction from our ExeContract is mined (validated) as well we can view our multiplication result. The contract featured a public variable lastResult. This can be called to get the current state. After calling the contract with 123, the balance would be 125.

/api /EthereumTest /exeContract /coins /subtractCoins /5
Now we subtract 5 coins , check the balance again and it should be 120.

Links

Back to top

Share anonymised data?

We find privacy important and strictly follow the new GDPR rules and regulations. This means we offer an opt-in for the use of Google Analytics.