Blockchain.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. using System;
  2. using System.Text;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using Newtonsoft.Json;
  7. using Console = HyperCube.Utils.AdvConsole;
  8. using System.Text.RegularExpressions;
  9. using Microsoft.AspNetCore.Components;
  10. //0xe5D682717955d6C35d465A3485625C64655a04f4 - HCB in rinkeby
  11. namespace HyperCube.Models
  12. {
  13. public struct TransactionObject
  14. {
  15. public string from;
  16. public string to;
  17. public string data;
  18. public string gas;
  19. }
  20. public class Blockchain
  21. {
  22. [Inject]
  23. AppData AppData { get; set; }
  24. public static Blockchain Dev
  25. {
  26. get {
  27. return loaded[0];
  28. }
  29. set { }
  30. }
  31. public static Dictionary<int, Blockchain> supported;
  32. public static string Connected
  33. {
  34. get {
  35. var bc = GetMain();
  36. if (bc != null)
  37. return $"{bc.name}";
  38. return "none";
  39. }
  40. }
  41. public static string URLdefault = "127.0.0.1";
  42. //public static int defaultPort = 8545;
  43. public static int defaultPort = 8666;
  44. public static string defaultName = "Ethereum dev network";
  45. public static bool blockChainsInitialized = false;
  46. public static Dictionary<int, Blockchain> loaded = new();
  47. public Dictionary<int, SmartContract> contracts = new();
  48. public Dictionary<string, SmartContract> contractNames = new();
  49. public int id;
  50. public int port;
  51. public string url;
  52. public string address = "";
  53. public string name;
  54. static Blockchain instance;
  55. public async static Task RegisterNetworks()
  56. {
  57. if (!blockChainsInitialized)
  58. {
  59. Console.WriteLine($"RegisterNetworks");
  60. var ethDev = new Blockchain(0, "Ethereum Dev Network", "127.0.0.1", 8545);
  61. var ethRinkeby = new Blockchain(1, "Ethereum Test Network", "127.0.0.1", 8666);
  62. await ethDev.Initialize();
  63. await ethRinkeby.Initialize();
  64. blockChainsInitialized = true;
  65. }
  66. }
  67. public static Blockchain Find(int id)
  68. {
  69. if (loaded.ContainsKey(id))
  70. return loaded[id];
  71. return null;
  72. }
  73. public async Task<string> Initialize()
  74. {
  75. if (!loaded.ContainsKey(id))
  76. loaded.Add(id, this);
  77. Console.WriteLine($"Initialize: {name}, loaded blockchains " + loaded.Count);
  78. string addr = await ListAccounts();
  79. await LoadContracts();
  80. Console.WriteLine("LoadContracts count " + contracts.Count);
  81. Console.WriteLine("connected " + Connected);
  82. return $"{name} {url}:{port}";
  83. }
  84. public static string dec2hex(int decValue, bool prefix = true)
  85. {
  86. string hexValue = decValue.ToString("X");
  87. if (prefix)
  88. hexValue = "0x" + hexValue;
  89. return hexValue;
  90. }
  91. //public static Blockchain GetInstance()
  92. //{
  93. // if (instance == null)
  94. // instance = new Blockchain(URLdefault, defaultPort);
  95. // return instance;
  96. //}
  97. //Blockchain()
  98. //{
  99. //}
  100. public Blockchain(int id, string name, string url, int port)
  101. {
  102. this.id = id;
  103. this.url = url;
  104. this.port = port;
  105. this.name = name;
  106. }
  107. public async Task<string> CreateBlockchainAccount(AccountModel account, string pass)
  108. {
  109. Console.WriteLine($"Run CreateBlockchainAccount {account}");
  110. var res = await RunFunction("personal_newAccount", $"\"{pass}\"");
  111. string query;
  112. if (account != null)
  113. {
  114. Console.WriteLine($"CreateBlockchainAccount {account}: {res}");
  115. //query = $"update aspnetusers set eth_address='{res}' where UserName='{account.Name}'";
  116. var addr = account.GetActualAddress(this);
  117. if (addr == null)
  118. query = $"insert into account_wallets (uuid, blockchain_id, account_uuid, password_plain) values ('{res}',{id},'{account.UUID}','{account.PWDHash}')";
  119. else
  120. query = $"update account_wallets set uuid='{res}', password_plain='{account.PWDHash}' where account_uuid='{account.UUID}' and blockchain_id={id}";
  121. account.SetActualAddress(res, this);
  122. Console.WriteLine($"CreateBlockchainAccount query {query}");
  123. await MySQLConnector.Instance().SQLInsert(query);
  124. }
  125. else
  126. Console.WriteLine($"CreateBlockchainAccount {id}: {res}");
  127. query = $"update blockchains set address_main='{res}' where id={id}";
  128. address = res;
  129. Console.WriteLine($"CreateBlockchainAccount query {query}");
  130. await MySQLConnector.Instance().SQLInsert(query);
  131. return res;
  132. }
  133. public static string bin2hex(string code)
  134. {
  135. //code = "0x".bin2hex('getCount()');
  136. var bytes = Encoding.UTF8.GetBytes(code);
  137. var hex = BitConverter.ToString(bytes).Replace("-", "");
  138. Console.WriteLine("hex " + hex);
  139. return "0x" + hex;
  140. }
  141. public static string zerofill(string hex, int charCount, bool hexPrefix0x)
  142. {
  143. if (hexPrefix0x)
  144. hex = hex.Remove(0, 2);
  145. Console.WriteLine($"zerofill input {hex} count {hex.Length}");
  146. var res = hex.PadLeft(charCount, '0');
  147. Console.WriteLine($"zerofill out {hex} count {res.Length}");
  148. return res;
  149. }
  150. public static Blockchain GetMain()
  151. {
  152. Console.WriteLine("GetMain blockchain_selected " + AccountModel.GetCurrent().blockchain_selected);
  153. try
  154. {
  155. if (loaded.Count > AccountModel.GetCurrent().blockchain_selected)
  156. return loaded[AccountModel.GetCurrent().blockchain_selected];
  157. else
  158. Console.WriteLine($"Error: blockchains loaded {loaded.Count}");
  159. }
  160. catch (Exception e)
  161. {
  162. Console.WriteLine("GetMain exception " + e.Message + ", stack trace:" + e.StackTrace);
  163. }
  164. return null;
  165. }
  166. public async Task<string> ListAccounts()
  167. {
  168. Console.WriteLine($"ListAccounts blockchain {id}");
  169. string answer = "no";
  170. //{ "jsonrpc":"2.0","method":"eth_getCode","params":["0x938cae6f6c21ed9d55196e96ef880f562e530553", "latest" ],"id":1}
  171. //string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x874c6a51e62680d4594cd2555ed8fa47b63ed253\",\"latest\"],\"id\":1}";
  172. string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_accounts\", \"params\":[],\"id\":1}";
  173. answer = await Post.PostRequestAsync(this, req);
  174. //string json = Encoding.UTF8.GetString(bytedata, 1, bytedata.Length - 1);
  175. //try
  176. //{
  177. Console.WriteLine("Json string " + answer);
  178. dynamic jsonDe = JsonConvert.DeserializeObject(answer);
  179. var res = (Newtonsoft.Json.Linq.JArray)jsonDe.result;
  180. if (res.Count > 0)
  181. {
  182. address = jsonDe.result[0];
  183. }
  184. else
  185. {
  186. address = await CreateEthAddress("test_Password_212");
  187. }
  188. var query = $"update blockchains set address_main='{address}' where id={id}";
  189. Console.WriteLine("query " + query);
  190. await MySQLConnector.Instance().SQLInsert(query);
  191. Console.WriteLine("Json addr " + jsonDe.result[0]);
  192. return address;
  193. }
  194. public async Task<string> CreateEthAddress(string pass)
  195. {
  196. return await CreateBlockchainAccount(null, pass);
  197. }
  198. public async Task LoadContracts()
  199. {
  200. MySQLConnector dbCon = MySQLConnector.Instance();
  201. contracts = await dbCon.SQLSelectContracts(id);
  202. foreach (var contract in contracts.Values)
  203. {
  204. await contract.LoadFunction();
  205. }
  206. }
  207. public static async Task<string> GetSHA3(string code)
  208. {
  209. var hex = bin2hex(code);
  210. var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"web3_sha3\",\"params\":[\"{hex}\"], \"id\":1}}";
  211. var answer = await Post.PostRequestAsync(Blockchain.Dev, req);
  212. dynamic jsonDe = JsonConvert.DeserializeObject(answer);
  213. Console.WriteLine("result " + jsonDe.result);
  214. return jsonDe.result;
  215. }
  216. public async Task<string> GetBalance(AccountModel account)
  217. {
  218. var addr = await account.GetOrCreateActualAddress(this);
  219. var ret = await RunFunction("eth_getBalance", $"\"{addr}\",\"latest\"");
  220. return ret;
  221. }
  222. public async Task<string> GetBalance(string address)
  223. {
  224. var ret = await RunFunction("eth_getBalance", $"\"{address}\",\"latest\"");
  225. return ret;
  226. }
  227. public async Task<string> GetBalance()
  228. {
  229. var ret = await RunFunction("eth_getBalance", $"\"{address}\",\"latest\"");
  230. var balanceInt = AccountModel.ConvertBalance(ret);
  231. return balanceInt.ToString();
  232. }
  233. public async Task<string> RunFunction(string name, string parms)
  234. {
  235. Console.WriteLine($"RunFunction {name} params {parms}");
  236. var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"{name}\",\"params\":[{parms}], \"id\":1}}";
  237. var answer = await Post.PostRequestAsync(this, req);
  238. dynamic jsonDe = JsonConvert.DeserializeObject(answer);
  239. Console.WriteLine("RunFunction result " + jsonDe.result);
  240. return jsonDe.result;
  241. //return "test answer";
  242. }
  243. public async Task<string> RunFunction2(string name, TransactionObject to)
  244. {
  245. Dictionary<string, string> paramDict = new Dictionary<string, string>();
  246. string parms = "{";
  247. if (to.from != "")
  248. paramDict["from"] = to.from;
  249. //parms += "\"from\":" + to.from;
  250. if (to.to != "")
  251. paramDict["to"] = to.to;
  252. //parms += "\"to\":"+to.to;
  253. int count = 0;
  254. foreach (var p in paramDict)
  255. {
  256. parms += $"\"{p.Key}\":\"{p.Value}\"";
  257. count++;
  258. if (paramDict.Count > count)
  259. {
  260. parms += ",";
  261. }
  262. }
  263. Console.WriteLine($"RunFunction {name} params {parms}");
  264. var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"{name}\",\"params\":[{parms}], \"id\":1}}";
  265. var answer = await Post.PostRequestAsync(this, req);
  266. dynamic jsonDe = JsonConvert.DeserializeObject(answer);
  267. Console.WriteLine("RunFunction result " + jsonDe.result);
  268. return jsonDe.result;
  269. //return "test answer";
  270. }
  271. async Task<string> mySHA3(string code)
  272. {
  273. var res = await Blockchain.GetSHA3(code);
  274. Console.WriteLine($"GetSHA3 {code} {res}");
  275. var ret = res.Substring(2, 8);
  276. return ret;
  277. }
  278. async Task<string> compileFunction(string input)
  279. {
  280. string pattern = @"function\s+(\S+)\s*\((.*)\).*";
  281. string replacement_name = "$1";
  282. string replacement_params = "$2";
  283. string resultname = Regex.Replace(input, pattern, replacement_name).Trim();
  284. string param_pattern = @"(uint8|address)";
  285. string final = resultname + "(";
  286. if (input != resultname)
  287. {
  288. string resultparams = Regex.Replace(input, pattern, replacement_params).Trim();
  289. if (input != resultparams)
  290. {
  291. int i = 0;
  292. foreach (Match match in Regex.Matches(resultparams, param_pattern))
  293. {
  294. if (i > 0)
  295. final += "," + match.Value;
  296. else
  297. final += match.Value;
  298. i++;
  299. }
  300. }
  301. }
  302. string function_processed = final + ")";
  303. string sha3 = await mySHA3(function_processed);
  304. return sha3;
  305. }
  306. public async Task<string> ImportERC20(string contractAddress)
  307. {
  308. //runfunction balanceOf totalSupply symbol name
  309. //RunContractRead
  310. TransactionObject to = new TransactionObject();
  311. to.to = contractAddress;
  312. //compile data
  313. to.data = await compileFunction("name()");
  314. var answer = await RunFunction2("eth_call", to);
  315. //var answer = await RunFunction("eth_sendTransaction", $"{{\"from\":\"{address}\",\"to\":\"{contractAddress}\",\"gas\":\"{gas}\", \"data\":\"{data}\", \"value\":\"{value}\"}}");
  316. Console.WriteLine("ImportERC20 "+ answer);
  317. return answer;
  318. }
  319. public async void RunContractRead()
  320. {
  321. string answer = "no";
  322. string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{\"to\":\"0x874c6a51e62680d4594cd2555ed8fa47b63ed253\", \"data\":\"0xa87d942c\"},\"latest\"],\"id\":1}";
  323. //string req = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"0x0000000000000000000000000000000000000000000000000000000000000004\"}";
  324. //Console.WriteLine($"req " + req);
  325. await Post.PostRequestAsync(this, req);
  326. //Console.WriteLine($"answer {answer} len {answer.Length}" );
  327. address = answer;
  328. }
  329. public async Task<string> SendTransaction(string fromAddress, string toAddress, int wei)
  330. {
  331. var sum = dec2hex(wei);
  332. var gas = await GetEstimatedGasTransaction(wei);
  333. Console.WriteLine($"SendTransaction from {fromAddress} to {toAddress} sum {sum}");
  334. var answer = await RunFunction("eth_sendTransaction", $"{{\"from\":\"{address}\",\"to\":\"{toAddress}\",\"gas\":\"{gas}\", \"data\":\"\", \"value\":\"{sum}\"}}");
  335. return answer;
  336. }
  337. //{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{ "from":"0xD81eeE6b39d9556c3067A3551A3FB2882b92F327", "to":"0x119b58faddcdbc09cafcd272530aa079cec10004", "gas":"0x31b2ef", "data":"0x11111111"}], "id":1}
  338. public async Task<string> RunContractWrite(string contractAddress, string data, string gas, string value)
  339. {
  340. Console.WriteLine("RunContract contractAddress " + contractAddress);
  341. var answer = await RunFunction("eth_sendTransaction", $"{{\"from\":\"{address}\",\"to\":\"{contractAddress}\",\"gas\":\"{gas}\", \"data\":\"{data}\", \"value\":\"{value}\"}}");
  342. //$"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_sendTransaction\",\"params\":[{{\"from\":\"{address}\",\"to\":\"{contractAddress}\",\"gas\":\"0x31b2ef\", \"data\":\"{data}\"}}], \"id\":1}}";
  343. //var answer = await Post.PostRequestAsync(req);
  344. //dynamic jsonDe = JsonConvert.DeserializeObject(answer);
  345. //Console.WriteLine("result " + answer);
  346. return answer;
  347. }
  348. public async Task<string> GetReceipt(string transactionAddress, bool returnAddress = false)
  349. {
  350. Console.WriteLine("transactionAddress " + transactionAddress);
  351. var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"{transactionAddress}\"], \"id\":1}}";
  352. var answer = await Post.PostRequestAsync(this, req);
  353. dynamic jsonDe = JsonConvert.DeserializeObject(answer);
  354. if (jsonDe.result != null)
  355. {
  356. var blockHash = jsonDe.result.blockHash;
  357. var blockNumber = jsonDe.result.blockNumber;
  358. var contractAddress = jsonDe.result.contractAddress;
  359. await MySQLConnector.Instance().SQLInsert($"insert into transactions (result, name) values ('{Convert.ToString(jsonDe.result)}', 'eth_getTransactionReceipt')");
  360. Console.WriteLine("result " + answer);
  361. if (returnAddress)
  362. return contractAddress;
  363. else
  364. return Convert.ToString(jsonDe.result);
  365. }
  366. else
  367. {
  368. Console.WriteLine("eth_getTransactionReceipt reuslt NULL " + transactionAddress);
  369. return null;
  370. }
  371. }
  372. //TODO add GAS!!!
  373. public async Task<object[]> AddContract(string name, string code, string bytecode)
  374. {
  375. Console.WriteLine("bytecode " + bytecode);
  376. var gas = await GetEstimatedGasContractAdd(bytecode);
  377. var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_sendTransaction\",\"params\":[{{\"from\":\"{address}\",\"gas\":\"{gas}\", \"data\":\"{bytecode}\"}}], \"id\":1}}";
  378. var answer = await Post.PostRequestAsync(this, req);
  379. dynamic jsonDe = JsonConvert.DeserializeObject(answer);
  380. var res = jsonDe.result;
  381. Console.WriteLine("result AddContract transactionAddress: " + res);
  382. if (res != null)
  383. {
  384. SmartContract newctr = new SmartContract(name, code, bytecode, this.id, gas);
  385. if (gas == null)
  386. gas = "invalid";
  387. long id = await MySQLConnector.Instance().SQLInsert($"insert into smart_contracts (code, bytecode, name, date_add, gas_cost, blockchain_id) values ('{code}','{bytecode}','{name}',NOW(), '{gas}', {this.id})");
  388. newctr.ID = (int) id;
  389. contracts.Add((int)id, newctr);
  390. contractNames.Add(name, newctr);
  391. return new object[] { res, newctr };
  392. }
  393. return null;
  394. }
  395. public async Task<string> GetEstimatedGasTransaction(int weiAmount)
  396. {
  397. if (weiAmount > 0)
  398. {
  399. var ret = await RunFunction("eth_estimateGas", $"{{\"from\":\"{address}\",\"value\":\"{dec2hex(weiAmount)}\"}}");
  400. Console.WriteLine("GetEstimatedGas " + ret);
  401. return ret;
  402. }
  403. return null;
  404. }
  405. public async Task<string> GetEstimatedGasContractAdd(string bytecode)
  406. {
  407. var ret = await RunFunction("eth_estimateGas", $"{{\"from\":\"{address}\",\"data\":\"{bytecode}\"}}");
  408. Console.WriteLine("GetEstimatedGasContractAdd " + ret);
  409. return ret;
  410. }
  411. public string GetAddress()
  412. {
  413. //this.address = address;
  414. return address;
  415. //post запрос
  416. }
  417. }
  418. }