Você está na página 1de 11

Introduction to Nethereum Blockchain in dotNetCore

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.Tran
sactionHash);
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"":""fun
ction""},{""constant"":true,""inputs"":[],""name"":""balance"",""outputs"":
[{""name"":"""",""type"":""uint256""}],""payable"":false,""type"":""functio
n""},{""inputs"":[{""name"":""initial"",""type"":""uint256""}],""payable"":
false,""type"":""constructor""}]";
private const string byteCode =
"0x6060604052341561000c57fe5b6040516020806101858339810160405280805190602001
90919050505b806000819055505b505b610143806100426000396000f300606060405260003
57c0100000000000000000000000000000000000000000000000000000000900463ffffffff
1680630173e3f41461005157806349fb396614610085578063b69ef8a8146100b9575bfe5b3
41561005957fe5b61006f60048080359060200190919050506100df565b6040518082815260
200191505060405180910390f35b341561008d57fe5b6100a36004808035906020019091905
0506100f8565b6040518082815260200191505060405180910390f35b34156100c157fe5b61
00c9610111565b6040518082815260200191505060405180910390f35b60008160005401905
0806000819055508090505b919050565b600081600054039050806000819055508090505b91
9050565b600054815600a165627a7a723058200085d6d7778b3c30ba2e3bf4af4c4811451f7
367109c1a9b44916d876cb67c5c0029";
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.