using System; using System.Text; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; using Console = HyperCube.Utils.AdvConsole; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Components; //0xe5D682717955d6C35d465A3485625C64655a04f4 - HCB in rinkeby namespace HyperCube.Models { public struct TransactionObject { public string from; public string to; public string data; public string gas; } public class Blockchain { [Inject] AppData AppData { get; set; } public static Blockchain Dev { get { return loaded[0]; } set { } } public static Dictionary supported; public static string Connected { get { var bc = GetMain(); if (bc != null) return $"{bc.name}"; return "none"; } } public static string URLdefault = "127.0.0.1"; //public static int defaultPort = 8545; public static int defaultPort = 8666; public static string defaultName = "Ethereum dev network"; public static bool blockChainsInitialized = false; public static Dictionary loaded = new(); public Dictionary contracts = new(); public Dictionary contractNames = new(); public int id; public int port; public string url; public string address = ""; public string name; static Blockchain instance; public async static Task RegisterNetworks() { if (!blockChainsInitialized) { Console.WriteLine($"RegisterNetworks"); var ethDev = new Blockchain(0, "Ethereum Dev Network", "127.0.0.1", 8545); var ethRinkeby = new Blockchain(1, "Ethereum Test Network", "127.0.0.1", 8666); await ethDev.Initialize(); await ethRinkeby.Initialize(); blockChainsInitialized = true; } } public static Blockchain Find(int id) { if (loaded.ContainsKey(id)) return loaded[id]; return null; } public async Task Initialize() { if (!loaded.ContainsKey(id)) loaded.Add(id, this); Console.WriteLine($"Initialize: {name}, loaded blockchains " + loaded.Count); string addr = await ListAccounts(); await LoadContracts(); Console.WriteLine("LoadContracts count " + contracts.Count); Console.WriteLine("connected " + Connected); return $"{name} {url}:{port}"; } public static string dec2hex(int decValue, bool prefix = true) { string hexValue = decValue.ToString("X"); if (prefix) hexValue = "0x" + hexValue; return hexValue; } //public static Blockchain GetInstance() //{ // if (instance == null) // instance = new Blockchain(URLdefault, defaultPort); // return instance; //} //Blockchain() //{ //} public Blockchain(int id, string name, string url, int port) { this.id = id; this.url = url; this.port = port; this.name = name; } public async Task CreateBlockchainAccount(AccountModel account, string pass) { Console.WriteLine($"Run CreateBlockchainAccount {account}"); var res = await RunFunction("personal_newAccount", $"\"{pass}\""); string query; if (account != null) { Console.WriteLine($"CreateBlockchainAccount {account}: {res}"); //query = $"update aspnetusers set eth_address='{res}' where UserName='{account.Name}'"; var addr = account.GetActualAddress(this); if (addr == null) query = $"insert into account_wallets (uuid, blockchain_id, account_uuid, password_plain) values ('{res}',{id},'{account.UUID}','{account.PWDHash}')"; else query = $"update account_wallets set uuid='{res}', password_plain='{account.PWDHash}' where account_uuid='{account.UUID}' and blockchain_id={id}"; account.SetActualAddress(res, this); Console.WriteLine($"CreateBlockchainAccount query {query}"); await MySQLConnector.Instance().SQLInsert(query); } else Console.WriteLine($"CreateBlockchainAccount {id}: {res}"); query = $"update blockchains set address_main='{res}' where id={id}"; address = res; Console.WriteLine($"CreateBlockchainAccount query {query}"); await MySQLConnector.Instance().SQLInsert(query); return res; } public static string bin2hex(string code) { //code = "0x".bin2hex('getCount()'); var bytes = Encoding.UTF8.GetBytes(code); var hex = BitConverter.ToString(bytes).Replace("-", ""); Console.WriteLine("hex " + hex); return "0x" + hex; } public static string zerofill(string hex, int charCount, bool hexPrefix0x) { if (hexPrefix0x) hex = hex.Remove(0, 2); Console.WriteLine($"zerofill input {hex} count {hex.Length}"); var res = hex.PadLeft(charCount, '0'); Console.WriteLine($"zerofill out {hex} count {res.Length}"); return res; } public static Blockchain GetMain() { Console.WriteLine("GetMain blockchain_selected " + AccountModel.GetCurrent().blockchain_selected); try { if (loaded.Count > AccountModel.GetCurrent().blockchain_selected) return loaded[AccountModel.GetCurrent().blockchain_selected]; else Console.WriteLine($"Error: blockchains loaded {loaded.Count}"); } catch (Exception e) { Console.WriteLine("GetMain exception " + e.Message + ", stack trace:" + e.StackTrace); } return null; } public async Task ListAccounts() { Console.WriteLine($"ListAccounts blockchain {id}"); string answer = "no"; //{ "jsonrpc":"2.0","method":"eth_getCode","params":["0x938cae6f6c21ed9d55196e96ef880f562e530553", "latest" ],"id":1} //string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x874c6a51e62680d4594cd2555ed8fa47b63ed253\",\"latest\"],\"id\":1}"; string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_accounts\", \"params\":[],\"id\":1}"; answer = await Post.PostRequestAsync(this, req); //string json = Encoding.UTF8.GetString(bytedata, 1, bytedata.Length - 1); //try //{ Console.WriteLine("Json string " + answer); dynamic jsonDe = JsonConvert.DeserializeObject(answer); var res = (Newtonsoft.Json.Linq.JArray)jsonDe.result; if (res.Count > 0) { address = jsonDe.result[0]; } else { address = await CreateEthAddress("test_Password_212"); } var query = $"update blockchains set address_main='{address}' where id={id}"; Console.WriteLine("query " + query); await MySQLConnector.Instance().SQLInsert(query); Console.WriteLine("Json addr " + jsonDe.result[0]); return address; } public async Task CreateEthAddress(string pass) { return await CreateBlockchainAccount(null, pass); } public async Task LoadContracts() { MySQLConnector dbCon = MySQLConnector.Instance(); contracts = await dbCon.SQLSelectContracts(id); foreach (var contract in contracts.Values) { await contract.LoadFunction(); } } public static async Task GetSHA3(string code) { var hex = bin2hex(code); var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"web3_sha3\",\"params\":[\"{hex}\"], \"id\":1}}"; var answer = await Post.PostRequestAsync(Blockchain.Dev, req); dynamic jsonDe = JsonConvert.DeserializeObject(answer); Console.WriteLine("result " + jsonDe.result); return jsonDe.result; } public async Task GetBalance(AccountModel account) { var addr = await account.GetOrCreateActualAddress(this); var ret = await RunFunction("eth_getBalance", $"\"{addr}\",\"latest\""); return ret; } public async Task GetBalance(string address) { var ret = await RunFunction("eth_getBalance", $"\"{address}\",\"latest\""); return ret; } public async Task GetBalance() { var ret = await RunFunction("eth_getBalance", $"\"{address}\",\"latest\""); var balanceInt = AccountModel.ConvertBalance(ret); return balanceInt.ToString(); } public async Task RunFunction(string name, string parms) { Console.WriteLine($"RunFunction {name} params {parms}"); var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"{name}\",\"params\":[{parms}], \"id\":1}}"; var answer = await Post.PostRequestAsync(this, req); dynamic jsonDe = JsonConvert.DeserializeObject(answer); Console.WriteLine("RunFunction result " + jsonDe.result); return jsonDe.result; //return "test answer"; } public async Task RunFunction2(string name, TransactionObject to) { Dictionary paramDict = new Dictionary(); string parms = "{"; if (to.from != "") paramDict["from"] = to.from; //parms += "\"from\":" + to.from; if (to.to != "") paramDict["to"] = to.to; //parms += "\"to\":"+to.to; int count = 0; foreach (var p in paramDict) { parms += $"\"{p.Key}\":\"{p.Value}\""; count++; if (paramDict.Count > count) { parms += ","; } } Console.WriteLine($"RunFunction {name} params {parms}"); var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"{name}\",\"params\":[{parms}], \"id\":1}}"; var answer = await Post.PostRequestAsync(this, req); dynamic jsonDe = JsonConvert.DeserializeObject(answer); Console.WriteLine("RunFunction result " + jsonDe.result); return jsonDe.result; //return "test answer"; } async Task mySHA3(string code) { var res = await Blockchain.GetSHA3(code); Console.WriteLine($"GetSHA3 {code} {res}"); var ret = res.Substring(2, 8); return ret; } async Task compileFunction(string input) { string pattern = @"function\s+(\S+)\s*\((.*)\).*"; string replacement_name = "$1"; string replacement_params = "$2"; string resultname = Regex.Replace(input, pattern, replacement_name).Trim(); string param_pattern = @"(uint8|address)"; string final = resultname + "("; if (input != resultname) { string resultparams = Regex.Replace(input, pattern, replacement_params).Trim(); if (input != resultparams) { int i = 0; foreach (Match match in Regex.Matches(resultparams, param_pattern)) { if (i > 0) final += "," + match.Value; else final += match.Value; i++; } } } string function_processed = final + ")"; string sha3 = await mySHA3(function_processed); return sha3; } public async Task ImportERC20(string contractAddress) { //runfunction balanceOf totalSupply symbol name //RunContractRead TransactionObject to = new TransactionObject(); to.to = contractAddress; //compile data to.data = await compileFunction("name()"); var answer = await RunFunction2("eth_call", to); //var answer = await RunFunction("eth_sendTransaction", $"{{\"from\":\"{address}\",\"to\":\"{contractAddress}\",\"gas\":\"{gas}\", \"data\":\"{data}\", \"value\":\"{value}\"}}"); Console.WriteLine("ImportERC20 "+ answer); return answer; } public async void RunContractRead() { string answer = "no"; string req = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{\"to\":\"0x874c6a51e62680d4594cd2555ed8fa47b63ed253\", \"data\":\"0xa87d942c\"},\"latest\"],\"id\":1}"; //string req = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"0x0000000000000000000000000000000000000000000000000000000000000004\"}"; //Console.WriteLine($"req " + req); await Post.PostRequestAsync(this, req); //Console.WriteLine($"answer {answer} len {answer.Length}" ); address = answer; } public async Task SendTransaction(string fromAddress, string toAddress, int wei) { var sum = dec2hex(wei); var gas = await GetEstimatedGasTransaction(wei); Console.WriteLine($"SendTransaction from {fromAddress} to {toAddress} sum {sum}"); var answer = await RunFunction("eth_sendTransaction", $"{{\"from\":\"{address}\",\"to\":\"{toAddress}\",\"gas\":\"{gas}\", \"data\":\"\", \"value\":\"{sum}\"}}"); return answer; } //{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{ "from":"0xD81eeE6b39d9556c3067A3551A3FB2882b92F327", "to":"0x119b58faddcdbc09cafcd272530aa079cec10004", "gas":"0x31b2ef", "data":"0x11111111"}], "id":1} public async Task RunContractWrite(string contractAddress, string data, string gas, string value) { Console.WriteLine("RunContract contractAddress " + contractAddress); var answer = await RunFunction("eth_sendTransaction", $"{{\"from\":\"{address}\",\"to\":\"{contractAddress}\",\"gas\":\"{gas}\", \"data\":\"{data}\", \"value\":\"{value}\"}}"); //$"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_sendTransaction\",\"params\":[{{\"from\":\"{address}\",\"to\":\"{contractAddress}\",\"gas\":\"0x31b2ef\", \"data\":\"{data}\"}}], \"id\":1}}"; //var answer = await Post.PostRequestAsync(req); //dynamic jsonDe = JsonConvert.DeserializeObject(answer); //Console.WriteLine("result " + answer); return answer; } public async Task GetReceipt(string transactionAddress, bool returnAddress = false) { Console.WriteLine("transactionAddress " + transactionAddress); var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"{transactionAddress}\"], \"id\":1}}"; var answer = await Post.PostRequestAsync(this, req); dynamic jsonDe = JsonConvert.DeserializeObject(answer); if (jsonDe.result != null) { var blockHash = jsonDe.result.blockHash; var blockNumber = jsonDe.result.blockNumber; var contractAddress = jsonDe.result.contractAddress; await MySQLConnector.Instance().SQLInsert($"insert into transactions (result, name) values ('{Convert.ToString(jsonDe.result)}', 'eth_getTransactionReceipt')"); Console.WriteLine("result " + answer); if (returnAddress) return contractAddress; else return Convert.ToString(jsonDe.result); } else { Console.WriteLine("eth_getTransactionReceipt reuslt NULL " + transactionAddress); return null; } } //TODO add GAS!!! public async Task AddContract(string name, string code, string bytecode) { Console.WriteLine("bytecode " + bytecode); var gas = await GetEstimatedGasContractAdd(bytecode); var req = $"{{ \"jsonrpc\":\"2.0\",\"method\":\"eth_sendTransaction\",\"params\":[{{\"from\":\"{address}\",\"gas\":\"{gas}\", \"data\":\"{bytecode}\"}}], \"id\":1}}"; var answer = await Post.PostRequestAsync(this, req); dynamic jsonDe = JsonConvert.DeserializeObject(answer); var res = jsonDe.result; Console.WriteLine("result AddContract transactionAddress: " + res); if (res != null) { SmartContract newctr = new SmartContract(name, code, bytecode, this.id, gas); if (gas == null) gas = "invalid"; 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})"); newctr.ID = (int) id; contracts.Add((int)id, newctr); contractNames.Add(name, newctr); return new object[] { res, newctr }; } return null; } public async Task GetEstimatedGasTransaction(int weiAmount) { if (weiAmount > 0) { var ret = await RunFunction("eth_estimateGas", $"{{\"from\":\"{address}\",\"value\":\"{dec2hex(weiAmount)}\"}}"); Console.WriteLine("GetEstimatedGas " + ret); return ret; } return null; } public async Task GetEstimatedGasContractAdd(string bytecode) { var ret = await RunFunction("eth_estimateGas", $"{{\"from\":\"{address}\",\"data\":\"{bytecode}\"}}"); Console.WriteLine("GetEstimatedGasContractAdd " + ret); return ret; } public string GetAddress() { //this.address = address; return address; //post запрос } } }