public static async Task <EthBlock> GetBlock(MySqlConnection connection, string blockHash, HexBigInteger blockNumber, Web3 cl, int blockchainID) { var block = await EthBlock.GetByNumber(connection, (UInt64)blockNumber.Value, blockchainID); if (block == null) { await _semaphore.WaitAsync(); try { block = await EthBlock.GetByNumber(connection, (UInt64)blockNumber.Value, blockchainID); if (block == null) { var apiBlock = await cl.Eth.Blocks.GetBlockWithTransactionsHashesByNumber.SendRequestAsync(blockNumber); block = new EthBlock { BlockHash = blockHash, BlockNumber = (UInt64)blockNumber.Value, Timestamp = TimestampHelper.UnixTimeStampToDateTime((double)apiBlock.Timestamp.Value), BlockchainID = blockchainID }; EthBlock.Insert(connection, block); } } finally { _semaphore.Release(); } } return(block); }
public void Timestamp_ValidDate() { DateTime date = TimestampHelper.UnixTimeStampToDateTime(1625319664); var expected = new DateTime(2021, 07, 03, 14, 41, 04, DateTimeKind.Local); Assert.Equal(expected, date); }
public async Task ExecuteEth(Source source) { Logger.WriteLine(source, "Syncing TRAC Market (ETH)"); DateTime now = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0, DateTimeKind.Utc); DateTime latestTimestamp; using (var connection = new MySqlConnection(OTHubSettings.Instance.MariaDB.ConnectionString)) { latestTimestamp = connection.ExecuteScalar <DateTime?>(@"select max(ticker_eth.Timestamp) from ticker_eth") ?? connection.ExecuteScalar <DateTime>(@"SELECT Min(b.Timestamp) FROM ethblock b where b.Timestamp >= COALESCE((select max(ticker_eth.Timestamp) from ticker_eth), (SELECT Min(b.Timestamp) FROM ethblock b))"); } var startDate = new DateTime(2019, 12, 26, 17, 0, 0, DateTimeKind.Utc); if (latestTimestamp < startDate) { latestTimestamp = startDate; } if ((now - latestTimestamp).TotalHours < 1) { return; } //CoinpaprikaAPI.Client client = new CoinpaprikaAPI.Client(); using (var wc = new WebClient()) { await using (var connection = new MySqlConnection(OTHubSettings.Instance.MariaDB.ConnectionString)) { await connection.OpenAsync(); for (DateTime date = latestTimestamp.Date; date.Date <= now; date = date.AddDays(1)) { if (date > now) { break; } DataTable rawData = new DataTable(); rawData.Columns.Add("Timestamp", typeof(DateTime)); rawData.Columns.Add("Price", typeof(decimal)); RootObject obj = null; for (int i = 0; i < 24; i++) { Int32 unixStartTimestamp = (Int32)(date.AddHours(i).Subtract(new DateTime(1970, 1, 1))).TotalSeconds; Int32 unixEndTimestamp = (Int32)(date.AddHours(i).AddHours(1).Subtract(new DateTime(1970, 1, 1))).TotalSeconds; await TimeConstraint; var data = wc.DownloadString( $"https://api.coingecko.com/api/v3/coins/origintrail/market_chart/range?vs_currency=eth&from={unixStartTimestamp}&to={unixEndTimestamp}"); obj = JsonConvert.DeserializeObject <RootObject>(data); if (obj?.prices == null) { continue; } foreach (List <double> ticker in obj.prices) { DateTime tickerTime = TimestampHelper.UnixTimeStampToDateTime(Convert.ToDouble(ticker[0].ToString().Substring(0, 10))); if (tickerTime <= latestTimestamp) { continue; } var row = rawData.NewRow(); row["Timestamp"] = tickerTime; row["Price"] = ticker[1]; rawData.Rows.Add(row); break; } } if (rawData.Rows.Count == 0) { continue; } await using (MySqlTransaction tran = await connection.BeginTransactionAsync(global::System.Data.IsolationLevel.Serializable)) { await using (MySqlCommand cmd = new MySqlCommand()) { cmd.Connection = connection; cmd.Transaction = tran; cmd.CommandText = "SELECT * FROM ticker_eth"; using (MySqlDataAdapter da = new MySqlDataAdapter(cmd)) { //da.UpdateBatchSize = 1000; using (MySqlCommandBuilder cb = new MySqlCommandBuilder(da)) { da.Update(rawData); await tran.CommitAsync(); if (obj != null && obj.prices != null && obj.prices.Any()) { var max = obj.prices.Max(v => TimestampHelper.UnixTimeStampToDateTime( Convert.ToDouble(v[0].ToString().Substring(0, 10)))); if (max > latestTimestamp) { latestTimestamp = max; } } } } } } } } } }
public static async Task <BeforePayoutResult> CanTryPayout(string nodeID, string offerId, string holdingAddress, string holdingStorageAddress, string litigationStorageAddress, string identity, int?blockchainID, string selectedAddress) { if (nodeID == null || offerId == null || holdingAddress == null || holdingStorageAddress == null || litigationStorageAddress == null || identity == null || blockchainID == null || selectedAddress == null) { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "Missing data in request." }); } await using (var connection = new MySqlConnection(OTHubSettings.Instance.MariaDB.ConnectionString)) { var holdingStorageAddressModel = await connection.QueryFirstOrDefaultAsync <ContractAddress>(ContractsSql.GetHoldingStorageAddressByAddress, new { holdingStorageAddress = holdingStorageAddress, blockchainID }); holdingStorageAddress = holdingStorageAddressModel?.Address; if (holdingStorageAddress == null) { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "OT Hub is not familiar with this holding storage smart contract address for this blockchain id " + blockchainID }); } var blockchainRow = await connection.QueryFirstAsync("SELECT * FROM blockchains where id = @id", new { id = blockchainID }); string blockchainName = blockchainRow.BlockchainName; string networkName = blockchainRow.NetworkName; string explorerTransactionUrl = blockchainRow.TransactionUrl; BlockchainType blockchainEnum = Enum.Parse <BlockchainType>(blockchainName); BlockchainNetwork networkNameEnum = Enum.Parse <BlockchainNetwork>(networkName); string nodeUrl = await connection.ExecuteScalarAsync <string>(@"SELECT BlockchainNodeUrl FROM blockchains WHERE id = @id", new { id = blockchainID }); var cl = new Web3(nodeUrl); var eth = new EthApiService(cl.Client); var ercContract = new Contract(eth, AbiHelper.GetContractAbi(ContractTypeEnum.ERC725, blockchainEnum, networkNameEnum), identity); Function keyHasPurposeFunction = ercContract.GetFunction("keyHasPurpose"); var abiEncode = new ABIEncode(); byte[] test = abiEncode.GetABIEncodedPacked(selectedAddress.HexToByteArray()); byte[] bytes = CalculateHash(test); bool hasPermission = await keyHasPurposeFunction.CallAsync <bool>(bytes, 1) || await keyHasPurposeFunction.CallAsync <bool>(bytes, 2); if (!hasPermission) { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "The address you have selected in MetaMask (" + selectedAddress + ") does not have permission to payout on the identity " + identity + ". You need to pick either your management wallet or operational wallet." }); } var holdingStorageAbi = AbiHelper.GetContractAbi(ContractTypeEnum.HoldingStorage, blockchainEnum, networkNameEnum); holdingAddress = (await connection.QueryFirstOrDefaultAsync <ContractAddress>(ContractsSql.GetHoldingAddressByAddress, new { holdingAddress = holdingAddress, blockchainID }))?.Address; if (holdingAddress == null) { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "OT Hub is not familiar with this holding smart contract address for this blockchain id " + blockchainID }); } var offerIdArray = offerId.HexToByteArray(); var holdingStorageContract = new Contract(new EthApiService(cl.Client), holdingStorageAbi, holdingStorageAddress); var getHolderStakedAmountFunction = holdingStorageContract.GetFunction("getHolderStakedAmount"); var holderStakedAmount = await getHolderStakedAmountFunction.CallAsync <BigInteger>(offerIdArray, identity); var getHolderPaymentTimestampFunction = holdingStorageContract.GetFunction("getHolderPaymentTimestamp"); var holderPaymentTimestamp = await getHolderPaymentTimestampFunction.CallAsync <BigInteger>(offerIdArray, identity); var getOfferHoldingTimeInMinutesFunction = holdingStorageContract.GetFunction("getOfferHoldingTimeInMinutes"); var offerHoldingTimeInMinutes = await getOfferHoldingTimeInMinutesFunction.CallAsync <BigInteger>(offerIdArray); var getHolderPaidAmountFunction = holdingStorageContract.GetFunction("getHolderPaidAmount"); var holderPaidAmount = await getHolderPaidAmountFunction.CallAsync <BigInteger>(offerIdArray, identity); if (holderStakedAmount <= 0) { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "The smart contract says this identity did not hold data for this job. The transaction will likely fail if you try to send this manually." }); } //long holdingTime = await connection.ExecuteScalarAsync<long>( // @"select HoldingTimeInMinutes from otoffer where OfferID = @offerID and blockchainID = @blockchainID", // new // { // offerId, // blockchainID // }); var latestBlockParam = BlockParameter.CreateLatest(); var block = await cl.Eth.Blocks.GetBlockWithTransactionsByNumber.SendRequestAsync(latestBlockParam); DateTime blockDate = TimestampHelper.UnixTimeStampToDateTime((double)block.Timestamp.Value); DateTime jobStartDate = await connection.ExecuteScalarAsync <DateTime>(@"SELECT Timestamp FROM otcontract_holding_offerfinalized where OfferID = @offerID and BlockchainID = @blockchainID", new { offerID = offerId, blockchainID }); DateTime jobEndDate = jobStartDate.AddMinutes((int)offerHoldingTimeInMinutes); if (blockDate > jobEndDate) { blockDate = jobEndDate; } BigInteger blockTimestamp = new BigInteger(TimestampHelper.DateTimeToUnixTimeStamp(blockDate)); var amountToTransfer = holderStakedAmount; amountToTransfer = amountToTransfer * (blockTimestamp - holderPaymentTimestamp); amountToTransfer = amountToTransfer / (offerHoldingTimeInMinutes * 60); decimal friendlyEstimatedPayout = Web3.Convert.FromWei(amountToTransfer); if (holderPaidAmount == holderStakedAmount) { var friendlyAmount = Web3.Convert.FromWei(holderPaidAmount); return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "The smart contract says you have been paid " + friendlyAmount + " TRAC for this job. The transaction will likely fail if you try to send this manually." }); } if (!String.IsNullOrWhiteSpace(litigationStorageAddress)) { litigationStorageAddress = (await connection.QueryFirstOrDefaultAsync <ContractAddress>(@"select Address from otcontract where Type = 9 AND Address = @litigationStorageAddress AND blockchainID = @blockchainID", new { litigationStorageAddress = litigationStorageAddress, blockchainID }))?.Address; if (litigationStorageAddress == null) { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "OT Hub is not familiar with this litigation storage smart contract address for this blockchain id " + blockchainID }); } Contract storageContract = new Contract((EthApiService)cl.Eth, AbiHelper.GetContractAbi(ContractTypeEnum.LitigationStorage, blockchainEnum, networkNameEnum), litigationStorageAddress); Function getLitigationStatusFunction = storageContract.GetFunction("getLitigationStatus"); Function getLitigationTimestampFunction = storageContract.GetFunction("getLitigationTimestamp"); BigInteger litigationTimestampInt = await getLitigationTimestampFunction.CallAsync <BigInteger>(latestBlockParam, offerIdArray, identity); Function getOfferLitigationIntervalInMinutesFunction = holdingStorageContract.GetFunction("getOfferLitigationIntervalInMinutes"); BigInteger litgationInterval = await getOfferLitigationIntervalInMinutesFunction.CallAsync <BigInteger>(latestBlockParam, offerIdArray) * 60; var status = await getLitigationStatusFunction.CallAsync <UInt16>(latestBlockParam, offerIdArray, identity); if (status == 1) //initiated { if (litigationTimestampInt + (litgationInterval * 2) >= block.Timestamp.Value) { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "The smart contract says 'Unanswered litigation in progress, cannot pay out'. The transaction will likely fail if you try to send this manually." }); } } else if (status == 2) //answered { if (litigationTimestampInt + (litgationInterval) >= block.Timestamp.Value) { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "The smart contract says 'Unanswered litigation in progress, cannot pay out'. The transaction will likely fail if you try to send this manually." }); } } else if (status == 0) //completed { //Do nothing as this is fine } else { return(new BeforePayoutResult { CanTryPayout = false, Header = "Stop!", Message = "The smart contract says 'Data holder is replaced or being replaced, cannot payout!'. The transaction will likely fail if you try to send this manually." }); } } return(new BeforePayoutResult { CanTryPayout = true, BlockchainExplorerUrlFormat = explorerTransactionUrl, EstimatedPayout = friendlyEstimatedPayout }); } }