기술/BlockChain

[Ethereum 개발기] Nethereum 개발기록

leatherjean 2018. 3. 11. 18:43
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 // This function will just execute a callback after it creates and encrypt a new account
    public void CreateAccount(string password, System.Action<stringstringstring> callback)
    {
        // We use the Nethereum.Signer to generate a new secret key
        var ecKey = Nethereum.Signer.EthECKey.GenerateKey();
 
        // After creating the secret key, we can get the public address and the private key with
        // ecKey.GetPublicAddress() and ecKey.GetPrivateKeyAsBytes()
        // (so it return it as bytes to be encrypted)
        var address = ecKey.GetPublicAddress();
        var privateKeyBytes = ecKey.GetPrivateKeyAsBytes();
        var privateKey = ecKey.GetPrivateKey();
 
        // Then we define a new KeyStore service
        var keystoreservice = new Nethereum.KeyStore.KeyStoreService();
 
        // And we can proceed to define encryptedJson with EncryptAndGenerateDefaultKeyStoreAsJson(),
        // and send it the password, the private key and the address to be encrypted.
        var encryptedJson = keystoreservice.EncryptAndGenerateDefaultKeyStoreAsJson(password, privateKeyBytes, address);
        // Finally we execute the callback and return our public address and the encrypted json.
        // (you will only be able to decrypt the json with the password used to encrypt it)
        callback(address, encryptedJson, privateKey);
    }
cs

C#에서 이더리움 account생성 - Nethereum 라이브러리를 바탕으로...


Nethereum.Signer의 정의는 다음과 같다.

: Nethereum signer library to sign and verify messages, RLP and transactions using an Ethereum account


account를 생성하기 위해서는 Nethereum.Signer.EthECKey.GenerateKey() 함수를 사용한다.

account는 secret Key로 생성되며, 

이 secret key를 이용해서

public key와 private key의 정보를 가져온다.


Nethereum에서는 보안 차원에서 json 형식으로 Keystore 서비스를 제공한다.


마지막엔 콜백함수를 호출해서 publickey와 KeystoreJson과 PrivateKey를 뱉어낸다.


해외 오픈소스를 분석해보자 - Account.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using Nethereum.JsonRpc.UnityClient;
using Nethereum.Hex.HexTypes;
using System;
 
public class Account : MonoBehaviour
{
    // 여기에 accountAddress를 정의한다. 퍼블릭키는 프라이빗키에서 나중에 추출해낼거임
    private string accountAddress;
    // 이게 address의 비밀키다. 여기에 너의 것을 입력하면 된다.
    private string accountPrivateKey = "0xe11e11e11e11e11e11e11e11e11e11e11e11e11e11e11e11e11e11e11e11e11e";
    // 이게 우리가 스마트컨트랙트를 위해 사용할 테스트넷이다. 우리는 kovan 테스트넷을 사용한다.
    private string _url = "https://kovan.infura.io";
 
    // 우리는 새로운 PingContractService를 정의한다. 이것은 우리가 컨트랙트를 위해 생성할 파일이다.
    private PingContractService pingContractService = new PingContractService();
 
    // 이것은 초기화에 사용된다.
    void Start()
    {
        // 첫째로 우리는 위에서 정의된 accountPrivateKey에서 퍼블릭키를 추출하고 할당할 이 함수를 부른다. 
        importAccountFromPrivateKey();
 
        // 그런 다음 getpings를 호출하여 pingFunction를 수행한 ping 수를 표시한다.
        // 이것은 그냥 블록체인에서 읽어올 것이기 때문에 0 가스를 소모한다. 이건 꽁짜다!
        StartCoroutine(getPings());
 
        // 이 후에 우리는 pingtransaction 함수를 실제로 컨트랙트와 상호작용하기 위해 부른다.
        // 이 함수는 우리의 컨트랙트에 새로운 트랜젝션을 생성하고 컴퓨팅 비용으로 가스를 소모한다.
        StartCoroutine(PingTransaction());
 
        StartCoroutine(getAccountBalance(accountAddress, (balance) => {
            Debug.Log("Account balance: " + balance);
        }));
 
        // 데모를 단순화하고 속도를 높이기위해 새 account 생성을 비활성화한다.
        // 언제든지 if를 제거하거나 새로운 계정을 만드려면 단순히 false를 true로 바꾸면 된다.
        // account 생성 및 잔액확인에 대한 코드 주석은 여기에서 볼 수 있다.
        // https://gist.github.com/e11io/88f0ae5831f3aa31651f735278b5b463
        var createNewAccount = false;
        if (createNewAccount == true)
        {
            CreateAccount("strong_password", (address, encryptedJson) => {
                Debug.Log(address);
                Debug.Log(encryptedJson);
                StartCoroutine(getAccountBalance(address, (balance) => {
                    Debug.Log(balance); // This will always give you 0, except if you are imposibly lucky.
                }));
            });
        }
 
    }
 
    public IEnumerator getPings()
    {
        // 우리는 새로운 Eth Call Unity Request으로 새로운 핑 리퀘스트를 생성한다.
        var pingsRequest = new EthCallUnityRequest(_url); // url은 코반 테스트넷
 
        var pingsCallInput = pingContractService.CreatePingsCallInput();
        Debug.Log("Getting pings...");
        // 그런다음 우리는 pingsCallinput과 함께 리퀘스트를 보낸다. 그리고 가장 최근 블록이 확인하기 위해 마이닝된다.
        // 그리고 응답을 기다린다.
        yield return pingsRequest.SendRequest(pingsCallInput, Nethereum.RPC.Eth.DTOs.BlockParameter.CreateLatest());
 
        if (pingsRequest.Exception == null)
        {
            // 만약 우리가 예외가 없다면 raw result를 그냥 보여줄것이고 
            // 그 결과는 우리의 서비스 함수인 decodePings 함수로 디코딩을 한다. 축하함!
            Debug.Log("Pings (HEX): " + pingsRequest.Result);
            Debug.Log("Pings (INT):" + pingContractService.DecodePings(pingsRequest.Result));
        }
        else
        {
            // 만약 unityRequest에 에러가 있다면 우리는 그냥 예외 에러를 보여준다.
            Debug.Log("Error submitting getPings tx: " + pingsRequest.Exception.Message);
        }
    }
 
    public IEnumerator PingTransaction()
    {
        // 함수를 위한 인코딩 된 값을 사용하여 트랜잭션 입력을 만든다.
        // 우리는 퍼블릭키와 프라이빗키가 필요할 것이다.
        // 우리가 우리의 contract에 보내려는 pingValue는 10000이다.
        // 가스량은 50000이다 이 경우에
        // 가스 가격은 25wei로 적는다. 너는 기본값을 얻기 위해 가스 가격을 null 로 보낼 수 있다.
        // 그리고 너가 전송하길 원하는 이더의 양인데, 이 컨트랙트는 이더리움 전송을 받지 않는다.
        // 그래서 우리는 이걸 0으로 놔둔다. 너는 수정할 수 있고 그게 어떻게 fail 하는지 볼 수 있다.
        var transactionInput = pingContractService.CreatePingTransactionInput(
            accountAddress,
            accountPrivateKey,
            new HexBigInteger(10000),
            new HexBigInteger(50000),
            new HexBigInteger(25),
            new HexBigInteger(0// 이더 양
        );
 
 
        // 여기에 우리는 새로운 서명된 트랜젝션 url과 프라이밋키 그리고 유저 address로 unity request를 create한다.
        // 이것은 자동적으로 트랜잭션에 서명할 것이다.
        var transactionSignedRequest = new TransactionSignedUnityRequest(_url, accountPrivateKey, accountAddress);
 
        // 이제 우리는 그것을 보내고 기다린다.
        Debug.Log("Sending Ping transaction...");
        yield return transactionSignedRequest.SignAndSendTransaction(transactionInput);
        if (transactionSignedRequest.Exception == null)
        {
            // 예외가 없으면 결과를 보여준다.
            Debug.Log("Ping tx submitted: " + transactionSignedRequest.Result);
        }
        else
        {
            // 예외가 있으면 exception 메시지를 보여준다
            Debug.Log("Error submitting Ping tx: " + transactionSignedRequest.Exception.Message);
        }
    }
 
 
    public void importAccountFromPrivateKey()
    {
        // 우리가 정의한 비밀키로부터 퍼블릭address를 획득한다.
        try
        {
            var address = Nethereum.Signer.EthECKey.GetPublicAddress(accountPrivateKey);
            // 그런 다음 퍼블릭키를 사용하여 accountAdderss 개인 변수를 정의한다.
            accountAddress = address;
        }
        catch (Exception e)
        {
            // 어떤 에러가 발견되면 보여준다.
            Debug.Log("Error importing account from PrivateKey: " + e);
        }
    }
 
 
    // 어카운트 개설과 잔액 체크에 대한 코드 코멘트는 여기에서 가능하다
    // https://gist.github.com/e11io/88f0ae5831f3aa31651f735278b5b463
    public void CreateAccount(string password, System.Action<stringstring> callback)
    {
        var ecKey = Nethereum.Signer.EthECKey.GenerateKey();
        var address = ecKey.GetPublicAddress();
        var privateKey = ecKey.GetPrivateKeyAsBytes();
 
        var addr = Nethereum.Signer.EthECKey.GetPublicAddress(privateKey.ToString());
        var keystoreservice = new Nethereum.KeyStore.KeyStoreService();
        var encryptedJson = keystoreservice.EncryptAndGenerateDefaultKeyStoreAsJson(password, privateKey, address);
 
        callback(address, encryptedJson);
    }
 
    public IEnumerator getAccountBalance(string address, System.Action<decimal> callback)
    {
        var getBalanceRequest = new EthGetBalanceUnityRequest(_url);
        yield return getBalanceRequest.SendRequest(address, Nethereum.RPC.Eth.DTOs.BlockParameter.CreateLatest());
        if (getBalanceRequest.Exception == null)
        {
            var balance = getBalanceRequest.Result.Value;
            callback(Nethereum.Util.UnitConversion.Convert.FromWei(balance, 18));
        }
        else
        {
            throw new System.InvalidOperationException("Get balance request failed");
        }
 
    }
 
}
cs

직접 주석을 +번역해서 달아보았다.

위 소스코드는 PingContract 컨트랙트를 사용하는 코드이다.

pingContract는 간결하다. ping을 보내면 pong에서 ping++을 진행한 값을 return 하는 것이다.

우리는 시작할때 자신의 지갑 private key를 입력하고,

스마트 컨트랙트의 네트워크 주소를 입력한다. 지금의 경우엔 kovan 테스트넷을 사용한다.


Start() 함수에서 importAccountFromPrivatekey 함수를 호출하면 

private 키에서 퍼블릭 어카운트 키를 추출해낸 뒤 변수에 저장한다.

StartCoroutine(getPings) 를 호출하여 ping의 값을 불러온다. 그냥 읽어오는거라 가스 0 소모