Skip to content

Nethereum sample #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions samples/Nethereum_sample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
*.swp
*.*~
project.lock.json
.DS_Store
*.pyc

# Visual Studio Code
.vscode

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
msbuild.log
msbuild.err
msbuild.wrn

# Visual Studio 2015
.vs/
25 changes: 25 additions & 0 deletions samples/Nethereum_sample/NethereumSample.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2035
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NethereumSample", "NethereumSample\NethereumSample.csproj", "{2FB6E152-E08B-47CA-84F7-1FDD5D0B5B79}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2FB6E152-E08B-47CA-84F7-1FDD5D0B5B79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2FB6E152-E08B-47CA-84F7-1FDD5D0B5B79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FB6E152-E08B-47CA-84F7-1FDD5D0B5B79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FB6E152-E08B-47CA-84F7-1FDD5D0B5B79}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B51E8970-A8BF-4FA9-BBA7-9D4085FFCEBC}
EndGlobalSection
EndGlobal
14 changes: 14 additions & 0 deletions samples/Nethereum_sample/NethereumSample/NethereumSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Common.Logging.Core" Version="3.4.1" />
<PackageReference Include="Nethereum.Portable" Version="2.5.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>

</Project>
132 changes: 132 additions & 0 deletions samples/Nethereum_sample/NethereumSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;

using Nethereum.Web3;
using Nethereum.JsonRpc.Client;
using Nethereum.Web3.Accounts.Managed;
using Nethereum.Hex.HexTypes;
namespace NethereumSample
{
class Program
{
//Please set your own parameters here!!!
//If you haven't got an account, you can uncomment the code in the region "new account",
//set the passphase of your future account in the variable accountPassphase, and skip the setting of the variable "account".
#region parameters
static string username = "<username>";
static string password = "<password>";
static string nodeUri = "<node_uri>";
static string account = "<account>";
static string accountPassphase = "<account_passphase>";
#endregion

static Web3 web3 = null;
static HttpClientHandler clientHandler = null;

static void Main(string[] args)
{
//uncomment the code in this region if you met the issue that the cert of the transaction node cannot be verified.
#region clietHandler init
//InitHandler();
#endregion

//This region is the key part to let Nethereum to connect to the transaction nodes directly without the blockchain connector!
//Following the code in this region, you can use the instance web3 to connect to the transaction nodes.
#region web3 init

var authValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", username, password)));
var authHeader = new AuthenticationHeaderValue("Basic", authValue);
web3 = new Web3(new RpcClient(new Uri(nodeUri), authHeader, null, clientHandler));
//Notice: clientHandler is null if the code in region "clietHandler init" remains commented.

//if you want to construct a Web3 instance with a managed account, you can use the code below:
//web3 = new Web3(new ManagedAccount(account, accountPassphase), new RpcClient(new Uri(nodeUri), authHeader, null, clientHandler));

#endregion

#region new account
//newAccount();
#endregion

#region account preparation
unlockAccount();
#endregion

#region test Transaction
testTransaction();
#endregion

Console.WriteLine("Sample code finished successfully!");
Console.ReadKey();
}
static void unlockAccount()
{
//As Parity and Quorum have different interface on jsonrpc method "personal_unlockAccount", we need to switch the unlock code manually.
//Please make sure that, in the two regions below, only one region is uncommented.

#region Parity unlock
var success = web3.Personal.UnlockAccount.SendRequestAsync(account, accountPassphase, new HexBigInteger(60)).Result;
#endregion

#region Quorum unlock
//var success = web3.Personal.UnlockAccount.SendRequestAsync(account, accountPassphase, 60).Result;
#endregion

Trace.Assert(success, "Unlock account failed! Please check your account address and the passphase!");
Console.WriteLine("Account unlock successed!\n");
}
static void testTransaction()
{
const string contractByteCode = "0x608060405234801561001057600080fd5b50602a60008190555060df806100276000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c14604e578063e5c19b2d146076575b600080fd5b348015605957600080fd5b50606060a0565b6040518082815260200191505060405180910390f35b348015608157600080fd5b50609e6004803603810190808035906020019092919050505060a9565b005b60008054905090565b80600081905550505600a165627a7a723058200e577c111b0ee4c2177cd4431abe395d21431e594a9441e820442f4ddbbe484f0029";
const string abi = @"[{""constant"":true, ""inputs"":[],""name"":""get"",""outputs"":[{""name"":"""",""type"":""int256""}],""payable"":false,""stateMutability"":""view"",""type"":""function""},{""constant"":false,""inputs"":[{""name"":""_a"",""type"":""int256""}],""name"":""set"",""outputs"":[],""payable"":false,""stateMutability"":""nonpayable"",""type"":""function""},{""inputs"":[],""payable"":false,""stateMutability"":""nonpayable"",""type"":""constructor""}]";
var receiptDeploy = web3.Eth.DeployContract.SendRequestAndWaitForReceiptAsync(abi, contractByteCode, account, new HexBigInteger(4700000), new HexBigInteger(0), new HexBigInteger(0), null).Result;

Trace.Assert(receiptDeploy.Status.Value.IsOne, "Contract Deployment failed. Check if it ran out of gas.");

Console.WriteLine("Contract deploying finished:");
Console.WriteLine(" ContractAddress: " + receiptDeploy.ContractAddress);
Console.WriteLine(" BlockHash: " + receiptDeploy.BlockHash);
Console.WriteLine(" TransactionHash: " + receiptDeploy.TransactionHash);
Console.WriteLine(" Blocknumber: " + receiptDeploy.BlockNumber.Value);
Console.WriteLine("\nSample code finished successfully!");

}
static void newAccount()
{
account = web3.Personal.NewAccount.SendRequestAsync(accountPassphase).Result;
Console.WriteLine("New account successed! The account address is:");
Console.WriteLine(account);
Console.WriteLine();
}
static void InitHandler()
{
//We just hard coded the issuer and the thumbPrint of the Root CA in the code,
//and we just want to verify if the Root CA of the transaction node is the hard-encoded ca.
const string issuer = "CN=Baltimore CyberTrust Root, OU=CyberTrust, O=Baltimore, C=IE";
const string thumbPrint = "D4DE20D05E66FC53FE1A50882C78DB2852CAE474";
clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, errors) =>
{
if (chain == null)
return false;
if (errors != SslPolicyErrors.None)
return false;
X509Certificate2 cert2 = null;

//the last element of chain.ChainElements should be the root ca
foreach (var x in chain.ChainElements)
{
cert2 = x.Certificate;
}
if (issuer == cert2.Issuer && thumbPrint == cert2.Thumbprint)
return true;
return false;
};
}
}
}
76 changes: 76 additions & 0 deletions samples/Nethereum_sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Nethereum Connection Sample

This is the sample code to show how we can let Nethereum to connect to our transaction nodes directly, without the blockchain connector.

To start quickly, you can just have a look on **Nethereum/Program.cs**. Note that the region "web3 initialization" is the most important part!

If you want to have a deep look on this sample code, please see the sections below for more infomation.

# Dependency

This sample is a .Net Core 2.0 console app. It has 3 dependencies from Nuget:

- package "Nethereum.Portable" Version="2.5.1"

- package "Common.Logging.Core" Version="3.4.1"

- package "Newtonsoft.Json" Version="11.0.2"

Note:
- Here the last two packages are dependencies of "Nethereum.Portable" and haven't beed directly used in the sample code.

# How to Run the Sample Code

1. Set the parameters:
- Go to the region "parameters", and set the parameters following the comments.
- If you don't have an account, see the section "New Account" for help.

2. Unlock the account:
- As Parity and Quorum have different interface of jsonrpc method "personal_unlockAccount", we need to switch the unlock code manually.
- For Quorum, please comment the code in the region "Parity unlock", and uncomment the code in the region "Quorum unlock".
- For Parity, please do the opposite.

3. Build and run the code! If every thing goes right, you'll see the logs like below:
```bash
Account unlock successed!

Contract deploying finished:
ContractAddress: 0xf8b90b91ea42954f620d159cdf157847037b72e5
BlockHash: 0xc5dc4a030a3d4e982c92218d13e11a36f1107dd4838b6a507234d6546e62af93
TransactionHash: 0x9d2ceeea587e99685bb0edb7a43a507fee67da3c365bc89493aa739f30b24633
Blocknumber: 14307

Sample code finished successfully!
```

Notice: If you meet the problem that the cert of the transaction node cannot be verified, see section "Cert Verification" for help.

# Explanation of the Code

1. The most important part of the code is the region "web3 init". After init the Web3 instance in this way, you can connect to the transaction node with the Web3 instance.

2. Difference about construct Web3 with or without a managed account:
- If a Web3 instance is constructed without a managed account, the jsonrpc method "eth_sendtransaction" is called when using this Web3 instance to send transaction.
- If a Web3 instance is constructed with a managed account, the jsonrpc method "personal_sendtransaction" is called when using this Web3 instance to send transaction.

# New Account

Enabling Nethereum to connect to the transaction node doesn't need a blockchain account, but our sample code does (because we deploy a contract in the code). If you didn't have an account before, you can apply for an account in our sample code by doing the following steps:

1. Uncomment the code in the region "new account".

2. In the region "parameters", set the variable accountPassphase. It will become the passphase of the new account.

3. In the region "parameters", skip the setting of the variable account, but do not comment it. (For Quorum and Parity, when you are applying for an account, you only need to give it the passphase, and then the account address will be given by Quorum and Parity).

Note:
- The new account also need to be unlocked, so do not forget to do the 2nd step in section "How to Run the Sample Code".
- It's not recommend to apply for a new account everytime you run the sample code (or other code that need an account).

# Cert Verification

If the cert of the transaction node cannot be verified, you can simply uncomment the code in the region "clietHandler init" to tackle the issue. Here is some notice about it:

1. From some experiments, we found the reason why the cert cannot be verified is that the client doesn't know the root ca (while the cert chain is a right cert chain).

2. We just hard coded the right root ca's issuer and thumbPrint in clientHandler.ServerCertificateCustomValidationCallback(), and verify the node's root ca.