/// <summary> /// Validate wallet contract. /// </summary> /// <param name="newContract"></param> /// <returns>Nothing if valid. But throw exception with reasons if invalid.</returns> private void ValidateContract(WalletContract newContract) { if (newContract.Name.Length < 8) { var emptyName = new byte[8 - newContract.Name.Length]; for (int i = 0; i < emptyName.Length; i++) { emptyName[i] = 0; } newContract.Name = emptyName.Concat(newContract.Name).ToArray(); } else if (newContract.Name.Length > 8) { throw new OverflowException("Contract Name can't be greater than 8 bytes."); } if (newContract.TokenName.Length > 8) { throw new OverflowException("Token Name can't be greater than 8 bytes."); } else if (newContract.TokenName.Length < 8) { var emptyName = new byte[8 - newContract.TokenName.Length]; for (int i = 0; i < emptyName.Length; i++) { emptyName[i] = 0; } newContract.TokenName = emptyName.Concat(newContract.TokenName).ToArray(); } if (newContract.TotalSupply < 0) { throw new OverflowException("Total supply must be positive value."); } }
/// <summary> /// Publish a wallet contract to blockchain and save the published to local data store. /// </summary> /// <param name="newContract">A to-be-published wallet contract.</param> /// <param name="creator">Private key of the creator</param> /// <returns>Return transaction ID if published. Throw exception if there is an error.</returns> public async Task <string> PublishContract(WalletContract newContract, BitcoinSecret creator) { try { ValidateContract(newContract); } catch (Exception) { throw; } var Data = $"{DomainHex}{newContract.NameHex}{newContract.TokenHex}{BitConverterExtension.GetHexBytes(newContract.TotalSupply)}" + $"{newContract.NoOfDecimal.ToString("x4")}{newContract.Conditions.ByteArrayToString()}"; var transaction = await insightAPI.BuildTransaction(creator, creator.GetAddress(), creator.GetAddress(), Data.ToLower()); var txId = await insightAPI.BroadcastTransaction(transaction); newContract.ID = txId.txid; var status = await insightAPI.GetSync(); newContract.StartingBlock = status.blockChainHeight; newContract.LastSyncedBlock = status.blockChainHeight; UpdateContract(newContract, true); return(newContract.ID); }
private void LoadData() { string path = $"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}CurrentContract.txt"; using (StreamReader sr = File.OpenText(path)) { string s; if ((s = sr.ReadLine()) != null) { contracttextbox.Text = s; MyContract = contractService.LoadContract(s); contractname.Text = MyContract.NameString; tokenname.Text = MyContract.TokenString; } } AllMyContract = contractService.FindLocalContract(); //view transaction in gridview var AllMyContract2 = AllMyContract.Select(x => new { x.ID, x.NameString, x.TokenString }).ToList(); dataGridView1.DataSource = AllMyContract2; dataGridView1.Columns[0].HeaderText = "รหัสสัญญา"; dataGridView1.Columns[1].HeaderText = "ชื่อสัญญา"; dataGridView1.Columns[2].HeaderText = "หน่วยนับ"; dataGridView1.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dataGridView1.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; dataGridView1.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; }
/// <summary> /// Create and broadcast a Electronic money wallet contract to blockchain. /// </summary> /// <param name="creator">Private key of the creator of wallet contract</param> /// <param name="Name">Maximum 8 bytes of binary value of token name </param> /// <param name="TokenName">Maximum 8 bytes of binary value of token unit</param> /// <param name="TotalSupply">Maximum supply of electronic money</param> /// <param name="NoOfDecimal">Maximum number of digit after decimal point. Dust value than this will be cut off</param> /// <param name="autopublish">Auto publish the contract to blockchain</param> /// <returns>A local-store of wallet contract. Throw exception if there is invalid provided value.</returns> public async Task <WalletContract> CreateContract(BitcoinSecret creator, byte[] Name, byte[] TokenName, decimal TotalSupply, ushort NoOfDecimal, bool autopublish = true) { var newContract = new WalletContract() { Name = Name, TokenName = TokenName, TotalSupply = TotalSupply, NoOfDecimal = NoOfDecimal, OwnerPublicAddress = creator.GetAddress().ToString(), LastSyncedBlock = 0, Conditions = new byte[8] }; try { ValidateContract(newContract); } catch (Exception) { throw; } if (autopublish) { await PublishContract(newContract, creator); } return(newContract); }
public async void InitializeContract() { var api = new DigibyteAPI(new APIOptions { BaseURL = Program.InsightAPI }); contractService = new ContractService(@"tsc-wallet.db", api); MyContract = contractService.LoadContract(currentContract); if (MyContract != null) { StatusLabel.Text = "สร้างสำเร็จ"; ContractIDtextBox.Text = MyContract.ID; NameLabel.Text = MyContract.NameString; TokenLabel.Text = MyContract.TokenString; TotalSupplyLabel.Text = MyContract.TotalSupply.ToString(); NoOfDecimalLabel.Text = MyContract.NoOfDecimal.ToString(); OwnerPublicLabel.Text = MyContract.OwnerPublicAddress; GenerateQrCode(); CoppyButton = true; } else { StatusLabel.Text = "สร้างไม่สำเร็จ"; StatusLabel.ForeColor = Color.Red; ContractIDtextBox.Text = currentContract; NameLabel.Text = "None"; TokenLabel.Text = "None"; TotalSupplyLabel.Text = "None"; NoOfDecimalLabel.Text = "None"; OwnerPublicLabel.Text = "None"; CoppyButton = false; } }
/// <summary> /// A builder of wallet service according to the given wallet contract. /// </summary> /// <param name="constract">Electronic money contract object</param> /// <param name="privateKey">The ledgers on this wallet service will be on behalf of the private key</param> /// <returns>A wallet service that manage the ledgers in the given wallet contract.</returns> public WalletService CreateWalletService(WalletContract constract, BitcoinSecret privateKey) { if (constract == null) { throw new ArgumentNullException("constract cannot be null."); } return(new WalletService(constract, this, db, privateKey, insightAPI, 0.01m)); }
/// <summary> /// Update or Insert wallet contract object to local data store. /// </summary> /// <param name="contract">A wallet contract object</param> /// <param name="reIndex">Re-index after insertion or updation. (Optional, default is false)</param> /// <returns>None.</returns> public void UpdateContract(WalletContract contract, bool reIndex = false) { collection.Upsert(contract); if (reIndex) { collection.EnsureIndex(c => c.ID); collection.EnsureIndex(c => c.Name); collection.EnsureIndex(c => c.OwnerPublicAddress); } }
/// <summary> /// A default account service constructor from a given a wallet service. /// </summary> /// <param name="contractService">A service object that manage contract.</param> /// <param name="contract">Current electronic money contract</param> /// <param name="walletService">A service object that manage ledgers</param> /// <param name="db">A LiteDB datastore</param> /// <returns>AccountService object is used to process individual account operation such as balance in one private key and one contract.</returns> internal AccountService(ContractService contractService, WalletContract contract, WalletService walletService, LiteDatabase db) { this.contractService = contractService; this.contract = contract; this.walletService = walletService; this.db = db; byte[] myByte = Encoding.ASCII.GetBytes(contract.ID); var encrypted = NBitcoin.Crypto.Hashes.RIPEMD160(myByte, myByte.Length); var hashID = encrypted.ByteArrayToString(); account = db.GetCollection <Account>($"Account-{hashID}"); }
/// <summary> /// Retrieve the wallet contract according to transaction ID from the local data store. If not found, retrieve through the blockchain API. /// </summary> /// <param name="txid">Bitcoin-compatible transaction ID</param> /// <returns>Return a wallet contract if found. Return null object if not found.</returns> public async Task <WalletContract> FindContract(string txid) { WalletContract existing = null; existing = collection.Find(c => c.ID == txid).FirstOrDefault(); if (existing != null) { return(existing); } else { try { var transaction = await insightAPI.GetTransactionInfo(txid); var detectedWallet = new WalletContract(); detectedWallet.ID = txid; detectedWallet.OwnerPublicAddress = transaction.GetOwnerAddress(); var op_return = transaction.GetOP_RETURN(); if (!op_return.StartsWith(DomainHex)) { return(null); } var startBit = 32; detectedWallet.NameHex = op_return.Substring(startBit, 16); startBit += 16; detectedWallet.TokenHex = op_return.Substring(startBit, 16); startBit += 16; detectedWallet.TotalSupply = BitConverterExtension.ToDecimal(op_return.Substring(startBit, 32)); startBit += 32; detectedWallet.NoOfDecimal = ushort.Parse(op_return.Substring(startBit, 4), System.Globalization.NumberStyles.HexNumber); startBit += 4; detectedWallet.Conditions = op_return.Substring(startBit, 16).StringToByteArray(); startBit += 16; detectedWallet.StartingBlock = transaction.blockheight; detectedWallet.LastSyncedBlock = transaction.blockheight; byte[] myByte = Encoding.ASCII.GetBytes(detectedWallet.ID); var encrypted = NBitcoin.Crypto.Hashes.RIPEMD160(myByte, myByte.Length); var hashID = encrypted.ByteArrayToString(); db.DropCollection($"Ledger-{hashID}"); db.DropCollection($"Account-{hashID}"); UpdateContract(detectedWallet, true); return(detectedWallet); } catch (Exception) { return(null); } } }
private void deletebutton_Click(object sender, EventArgs e) { string path = $"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}CurrentContract.txt"; if (File.Exists(path)) { File.WriteAllText(path, string.Empty); } MessageBox.Show("ลบรหัสสัญญาที่เลือกไว้สำเร็จ"); contracttextbox.Text = ""; contractname.Text = ""; tokenname.Text = ""; MyContract = null; }
private async void addbutton_Click(object sender, EventArgs e) { Contract_id = contracttextbox.Text; var result = await contractService.FindContract(contracttextbox.Text); if (result == null) { MessageBox.Show("ไม่ค้นพบรหัสสัญญาใน Block explorer"); } else { MyContract = contractService.LoadContract(Contract_id); string connectionString = $"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}tsc-wallet.db"; using (var db = new LiteDatabase(connectionString)) //Insert Wallet { var collection = db.GetCollection <MonitorContract>("Contracts"); MonitorContract existing = null; existing = collection.Find(c => c.contract_id == Contract_id).FirstOrDefault(); if (existing == null) { var contract = new MonitorContract { contract_id = Contract_id, owner_pubkey = MyContract.OwnerPublicAddress, blockheight = 0 }; collection.Insert(contract); } } string path = $"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}CurrentContract.txt"; if (File.Exists(path)) { File.WriteAllText(path, string.Empty); using (StreamWriter sw = File.CreateText(path)) { sw.WriteLine(MyContract.ID); } } MessageBox.Show("เลือกรหัสสัญญาสำเร็จ"); LoadData(); } }
/// <summary> /// A default constructor of Wallet service. (Need to initialized from a ContractService object.) /// </summary> /// <param name="contract">A wallet contract object</param> /// <param name="contractService">A wallet contract service object</param> /// <param name="db">LiteDB database object</param> /// <param name="userPrivateKey">User's private key</param> /// <param name="api">Insight-based compatible API (>0.4)</param> /// <param name="TransferFee">Fee for token transfer</param> /// <returns>A wallet service is used to obtain all the ledger from the wallet. Private key is used to create the ledger on the user's behalf.</returns> internal WalletService(WalletContract contract, ContractService contractService, LiteDatabase db, BitcoinSecret userPrivateKey, IInsightAPI api, decimal TransferFee) { this.TransferFee = TransferFee; this.userPrivateKey = userPrivateKey; this.contractService = contractService; this.contract = contract; this.api = api; this.db = db; byte[] myByte = Encoding.ASCII.GetBytes(contract.ID); var encrypted = NBitcoin.Crypto.Hashes.RIPEMD160(myByte, myByte.Length); var hashID = encrypted.ByteArrayToString(); ledger = db.GetCollection <Ledger>($"Ledger-{hashID}"); account = db.GetCollection <Account>($"Account-{hashID}"); ownerAddress = new BitcoinPubKeyAddress(contract.OwnerPublicAddress, contractService.MainNetwork); }
public static Ledger ToLedger(this ApiTransaction transaction, WalletContract contract, ContractService service) { var ledger = new Ledger() { TxId = transaction.txid, Blockheight = transaction.blockheight, Time = new DateTime(1970, 1, 1).AddSeconds(transaction.blocktime) }; var domain = BitConverter.ToString(Encoding.Default.GetBytes("RMUTSB.AC.TH")).Replace("-", "").ToLower(); //var domain = "54534357414c4c4554"; var data = transaction.GetOP_RETURN(); try { if (data.StartsWith(domain)) { return(null); } var contractNameHex = data.Substring(0, 16); if (contract.NameHex == contractNameHex) { ledger.Operation = (OperationCode)(ushort.Parse(data.Substring(16, 4), System.Globalization.NumberStyles.HexNumber)); var publicAddress = transaction.vin.First(v => !string.IsNullOrEmpty(v.addr)).addr; var publicKey = new BitcoinPubKeyAddress(publicAddress, service.MainNetwork); ledger.TokenSenderHash = publicKey.ExtractWitnessProgram(); ledger.TokenReceiverHashHex = data.Substring(20, 40); ledger.Amount = BitConverterExtension.ToDecimal(data.Substring(60, 32).StringToByteArray()); if (data.Length > 92) { ledger.ReferenceCode = Encoding.Default.GetString(data.Substring(92).StringToByteArray()); } return(ledger); } else { return(null); } } catch (Exception e) { Debug.WriteLine(e.Message); return(null); } }
private async void CreateContract() { InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Visible", true); InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Text", "กำลังสร้างสัญญาเงินอิเล็กทรอนิกส์"); var contract = Encoding.UTF8.GetBytes(contracttextbox.Text);; var token = Encoding.UTF8.GetBytes(tokentextbox.Text);; decimal amount = Decimal.Parse(AmountTextBox.Text); ushort noOfdecimal = Convert.ToUInt16(NoOfDecimalTextBox.Text); WalletContract mycontract = null; var api = new DigibyteAPI(new APIOptions { BaseURL = Program.InsightAPI }); contractService = new ContractService(@"tsc-wallet.db", api); mycontract = await contractService.CreateContract(privateKey, contract, token, amount, noOfdecimal); SetControlPropertyThreadSafe(progressBar1, "Value", 2); contractID = mycontract.ID; MessageBox.Show("รหัสสัญญา :" + contractID); //test Show Contract ID int CountConnect = 0; bool triggerContract = false; transactions = new TransBlockchain(); while (CountConnect <= 15) //find contractID in blockchain { InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Text", "กำลังตรวจสอบธุรกรรมการสร้างสัญญา"); try { var response = await client.GetAsync("/api/tx/" + contractID); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsStringAsync(); transactions = JsonConvert.DeserializeObject <TransBlockchain>(result); if (transactions.time != 0 && transactions.blocktime != 0) { triggerContract = true; break; } else { CountConnect++; Thread.Sleep(5000); } } catch { CountConnect++; Thread.Sleep(5000); } } SetControlPropertyThreadSafe(progressBar1, "Value", 3); if (!triggerContract) { InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Visible", false); SetControlPropertyThreadSafe(progressBar1, "Visible", false); MessageBox.Show("ไม่ค้นพบสัญญาเงินอิเล็กทรอนิกส์ในบล็อกเชน :" + contractID); return; } resultContract = await contractService.FindContract(contractID); SetControlPropertyThreadSafe(progressBar1, "Value", 4); if (resultContract == null) //if dont seen this transaction. exite method { InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Visible", false); SetControlPropertyThreadSafe(progressBar1, "Visible", false); MessageBox.Show("การสร้างรหัสสัญญาไม่สำเร็จ"); return; } //Check Block of Contract and LastBlock. because if block of contract is lastblock, calculating of cryptocurrency balance will be issue bool triggerAllow = false; CountConnect = 0; while (CountConnect <= 10) { try { int ContractBlock = await api.GetBlockHeight(transactions.blockhash); var LastSync = await api.GetSync(); int Lastblock = LastSync.blockChainHeight; if (ContractBlock != Lastblock) { triggerAllow = true; break; } else { CountConnect++; Thread.Sleep(3000); } } catch { CountConnect++; Thread.Sleep(3000); } } SetControlPropertyThreadSafe(progressBar1, "Value", 5); if (!triggerAllow) { InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Visible", false); InvokeHelp.SetControlPropertyThreadSafe(progressBar1, "Visible", false); MessageBox.Show("ขณะนี้ยังไม่สามารถออกเงินได้ ลองอีกครั้ง"); return; } Thread.Sleep(5000); //wait mined int issuedRepeat = 2; start: InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Text", "กำลังออกเงิน"); var walletService = contractService.CreateWalletService(resultContract, privateKey); //Create issued Ledger var ledger1 = walletService.CreateLedger(OperationCode.Issue); await walletService.BroadcastLedger(ledger1); SetControlPropertyThreadSafe(progressBar1, "Value", 6); MessageBox.Show("รหัสการออกเงิน :" + ledger1.TxId); //test Show issued ID transactions = new TransBlockchain(); int Count = 0; bool IssueTrigger = false; while (Count <= 15) { InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Text", "กำลังตรวจสอบธุรกรรมการออกเงิน"); try { var response2 = await client.GetAsync("/api/tx/" + ledger1.TxId); response2.EnsureSuccessStatusCode(); var result = await response2.Content.ReadAsStringAsync(); transactions = JsonConvert.DeserializeObject <TransBlockchain>(result); if (transactions.time != 0 && transactions.blocktime != 0) { IssueTrigger = true; break; } else { Count++; Thread.Sleep(5000); } } catch { Count++; Thread.Sleep(5000); } } SetControlPropertyThreadSafe(progressBar1, "Value", 7); if (!IssueTrigger) //if { InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Visible", false); SetControlPropertyThreadSafe(progressBar1, "Visible", false); MessageBox.Show("หมดเวลาการรอ กรุณาตรวจสอบรหัสธุรกรรมการออกเงินด้วยตัวเองที่ตัวสำรวจบล็อก :" + transactions.txid); return; //exite ,method } var txContract = await api.GetTransactionInfo(contractID); var txIssued = await api.GetTransactionInfo(ledger1.TxId); if (txContract.blocktime > txIssued.blocktime) //if Contract blocktime more than Issued blocktime. New Issued again. { if (issuedRepeat > 0) { issuedRepeat--; goto start; } else { InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Visible", false); SetControlPropertyThreadSafe(progressBar1, "Visible", false); MessageBox.Show("หมดเวลาการรอ กรุณาตรวจสอบรหัสธุรกรรมการออกเงินด้วยตัวเองที่ตัวสำรวจบล็อก :" + txIssued.txid); return; } } InvokeHelp.SetControlPropertyThreadSafe(progressLabel, "Text", "สร้างสัญญาเงินอิเล็กทรอนิกส์สำเร็จ"); triggerFinish = true; Action action = new Action(FinishCreating); this.BeginInvoke(action); }