[Ethereum 개발기] Nethereum 개발기록
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<string, string, string> 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<string, string> 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 소모