/// <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);
        }
예제 #3
0
        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);
                }
            }
        }
예제 #10
0
        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;
        }
예제 #11
0
        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);
            }
        }
예제 #14
0
        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);
        }