functioncall.CallAsync<string>() in Nethereum returning null - c#

I tried using returnString.CallAsync(); to a smart contract function that returns bytes or string but Im getting null. Would you know why this is the case with functions that return string or bytes and not int? I am using nethereum as the .net client for ethereum.
here is my contract
contract A {
string myString = "someString";
function A(int multiplier) {
}
function getMyString() returns (bytes) {
bytes memory b3 = bytes(myString);
return b3;
}
}
Here is my .net nethereum code:
var senderAddress = "0x12890d2cce102216644c59daE5baed380d84830c";
var password = "password";
var abi = #"[{""constant"":false,""inputs"":[],""name"":""getMyString"",""outputs"":[{""name"":"""",""type"":""bytes""}],""payable"":false,""type"":""function""},{""inputs"":[{""name"":""multiplier"",""type"":""int256""}],""payable"":false,""type"":""constructor""}]";
var byteCode =
"0x60a0604052600a60608190527f736f6d65537472696e6700000000000000000000000000000000000000000000608090815261003e9160009190610060565b50341561004757fe5b6040516020806102a383398101604052515b5b50610100565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a157805160ff19168380011785556100ce565b828001600101855582156100ce579182015b828111156100ce5782518255916020019190600101906100b3565b5b506100db9291506100df565b5090565b6100fd91905b808211156100db57600081556001016100e5565b5090565b90565b6101948061010f6000396000f300606060405263ffffffff60e060020a6000350416637e7b8a968114610021575bfe5b341561002957fe5b6100316100b1565b604080516020808252835181830152835191928392908301918501908083838215610077575b80518252602083111561007757601f199092019160209182019101610057565b505050905090810190601f1680156100a35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100b9610156565b6100c1610156565b6000805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156101475780601f1061011c57610100808354040283529160200191610147565b820191906000526020600020905b81548152906001019060200180831161012a57829003601f168201915b505050505090508091505b5090565b604080516020810190915260008152905600a165627a7a72305820eb300cf43a9ba95dde6cd63161d8229e787f1e4daf4f549c303011176546f2ba0029";
var web3 = new Web3.Web3();
var unlockAccountResult =
await web3.Personal.UnlockAccount.SendRequestAsync(senderAddress, password, 120);
var transactionHash =
await web3.Eth.DeployContract.SendRequestAsync(abi, byteCode, senderAddress, 1);
var mineResult = await web3.Miner.Start.SendRequestAsync(6);
var receipt = await web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(transactionHash);
while (receipt == null)
{
Thread.Sleep(5000);
receipt = await web3.Eth.Transactions.GetTransactionReceipt.SendRequestAsync(transactionHash);
}
mineResult = await web3.Miner.Stop.SendRequestAsync();
var contractAddress = receipt.ContractAddress;
var contract = web3.Eth.GetContract(abi, contractAddress);
var returnString = contract.GetFunction("getMyString");
byte[] result2 = await returnString.CallAsync<byte[]>();
while (result2 == null)
{
Thread.Sleep(5000);
result2 = await returnString.CallAsync<byte[]>();
}

When running on local Ganache, this contract works fine (the correct string is returned). No need to mine or to wait.
I've created a example project here https://github.com/StefH/Solidity-Examples/tree/master/SmartContracts/Examples/GetString.
Go to Solidity folder and run npm i and npm run build to generate C# interface and implementation files.
In the ConsoleApp folder there is a example dotnet core console app which outputs like:
Blockchain - Ganache - ConsoleApp
________________________________________________________________________________
DeployContractAsync
Call GetMyStringCallAsync
result = `someString`
________________________________________________________________________________

Related

Azure ArmClient Rename & Copy DB Operations

A bit of background, I am looking to replace existing code in a C# App from the existing Microsoft.Azure.Management.Fluent (now deprecated) to the newer Azure.ResourceManager components.
Existing code to copy a database:
public async Task<bool> CopyDb(string? server, string? fromName, string? toName)
{
_log.LogInformation("Connecting to Azure");
var azure = GetAzureObject();
var servers = await azure.SqlServers.ListAsync();
var fromServer = servers.FirstOrDefault(f => server != null && server.Contains(f.Name));
if (fromServer == null)
{
throw new InvalidOperationException("Unable to find original database server");
}
var toNameBackup = $"{toName}-Old";
var existingDbs = await fromServer.Databases.ListAsync();
var fromDB = existingDbs.FirstOrDefault(f => f.Name.Equals(fromName));
if (fromDB == null)
{
throw new InvalidOperationException("Unable to find original database");
}
if (existingDbs.Any(a => a.Name.Equals(toNameBackup, StringComparison.OrdinalIgnoreCase))
&& existingDbs.Any(a => a.Name.Equals(toName, StringComparison.OrdinalIgnoreCase)))
{
_log.LogInformation("Deleting any existing backup called {0}", toNameBackup);
await fromServer.Databases.DeleteAsync(toNameBackup);
}
if (existingDbs.Any(a => a.Name.Equals(toName, StringComparison.OrdinalIgnoreCase)))
{
_log.LogInformation("Renaming target database from {0} to {1} (if exists)", toName, toNameBackup);
await (await fromServer.Databases.GetAsync(toName)).RenameAsync(toNameBackup);
}
_log.LogInformation("Copying database from from {0} to {1}", fromName, toName);
var result = await fromServer.Databases.
Define(toName).
WithSourceDatabase(fromDB).
WithMode(Microsoft.Azure.Management.Sql.Fluent.Models.CreateMode.Copy).CreateAsync();
return result != null;
}
private Microsoft.Azure.Management.Fluent.IAzure GetAzureObject()
{
var clientId = _configuration["AzureClientId"];
var clientSecret = _configuration["AzureClientSecret"];
var tenantId = _configuration["AzureTenantId"];
var subscriptionId = _configuration["AzureSubscriptionId"];
var credentials = Microsoft.Azure.Management.ResourceManager.Fluent.SdkContext.AzureCredentialsFactory.FromServicePrincipal(
clientId: clientId,
clientSecret: clientSecret,
tenantId: tenantId,
environment: Microsoft.Azure.Management.ResourceManager.Fluent.AzureEnvironment.AzureGlobalCloud);
return Microsoft.Azure.Management.Fluent.Azure.Configure().Authenticate(credentials).WithSubscription(subscriptionId);
}
The newer components all work with resources and I've been struggling how to do a couple operations with the newer Azure.ArmClient. I've been able to query with it finding my SQL server and databases. I can even delete some DBs, but I'm unable to work out how to rename or copy databases like the above code. I know there are alternative ways to do this directly in SQL, but I'd prefer to see how to do it in code.
I have had a look around MS docs, I can only find information on the object definitions but no examples.
I have managed to get down to the point of renaming:-
var backupDb = fromServer.GetSqlDatabase(toName);
if (backupDb != null && backupDb.Value != null)
{
// What do I pass in to the definition?
var moveDefinition = new SqlResourceMoveDefinition()
{
// What to set here?
};
await (await backupDb.Value.GetAsync()).Value.RenameAsync(moveDefinition);
}
I'm not sure on how to define the SqlResourceMoveDefinition. I also can't work out at all how to perform the copy like in the older SDK.
Anyone have any guides on how to achieve these operations in C#?
Managed to work it out after eventually working from https://learn.microsoft.com/en-us/dotnet/azure/sdk/resource-management?tabs=PowerShell. There may be better ways to do this, and I'll edit the answer when I find them if others don't by then!
public async Task<bool> CopyDb(string? server, string? fromName, string? toName)
{
_log.LogInformation("Connecting to Azure");
var azure = GetAzureSubscription();
var servers = azure.GetSqlServers().ToList();
var fromServer = servers.SingleOrDefault(f => server != null && f.Data != null && server.Contains(f.Data.Name));
if (fromServer == null)
{
throw new InvalidOperationException("Unable to find original database server");
}
var oldName = $"{toName}-Old";
var databases = fromServer.GetSqlDatabases();
_log.LogInformation("Check for any existing backup called {0}", oldName);
if (await databases.ExistsAsync(oldName))
{
_log.LogInformation("Deleting for any existing backup called {0}", oldName);
var oldBackup = await databases.GetAsync(oldName);
await oldBackup.Value.DeleteAsync(WaitUntil.Completed);
}
_log.LogInformation("Check target database {0} exists", toName, oldName);
if (await databases.ExistsAsync(toName))
{
_log.LogInformation("Renaming target database from {0} to {1}", toName, oldName);
var toDbBackup = await databases.GetAsync(toName);
var resourceIdString = toDbBackup.Value.Data.Id.Parent?.ToString();
var newResourceId = new ResourceIdentifier($"{resourceIdString}/databases/{oldName}");
var moveDefinition = new SqlResourceMoveDefinition(newResourceId);
var toDb = await toDbBackup.Value.GetAsync();
await toDb.Value.RenameAsync(moveDefinition);
}
_log.LogInformation("Copying database from from {0} to {1}", fromName, toName);
var fromDb = await databases.GetAsync(fromName);
var result = await databases.CreateOrUpdateAsync(WaitUntil.Completed, toName, fromDb.Value.Data);
_log.LogInformation("Operation completed!");
return result.HasValue;
}
private SubscriptionResource GetAzureSubscription()
{
var configValue = _configuration["AzureSubscriptionId"];
var subscriptionId = new ResourceIdentifier($"/subscriptions/{configValue}");
return GetAzureArmClient().GetSubscriptionResource(subscriptionId);
}
private ArmClient GetAzureArmClient()
{
var clientId = _configuration["AzureClientId"];
var clientSecret = _configuration["AzureClientSecret"];
var tenantId = _configuration["AzureTenantId"];
var credentials = new ClientSecretCredential(
clientId: clientId,
clientSecret: clientSecret,
tenantId: tenantId);
return new ArmClient(credentials);
}

Capturing full screen screenshot using C# Selenium WebDriver html2canvas returns null

I have problem getting output from html2canvas JS library within Chrome automated by selenium.
Response is always null, but I can see in the Chrome console that code executed successfully and screenshot has been encoded
public byte[] TakeScreenshot(string fileName)
{
this.logger.Debug($"Taking screenshot");
var seleniumDownloadPath = this.seleniumEngine.GetDownloadPath();
IJavaScriptExecutor js = Driver as IJavaScriptExecutor;
var html2canvasJs = System.IO.File.ReadAllText(Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), this.seleniumEngine.GetHtml2CanvasPath()));
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(60));
var response = new object { };
js.ExecuteScript(html2canvasJs);
string generateScreenshotJS =
var canvasImgContentDecoded;
#"function genScreenshot () {
html2canvas(document.body).then(function(canvas) {
window.canvasImgContentDecoded = canvas.toDataURL(""image/png"");
console.log(window.canvasImgContentDecoded);
return window.canvasImgContentDecoded;
});
}
genScreenshot();";
response = js.ExecuteScript(generateScreenshotJS);
}
I also tried solution from this Here but the behavior was unstable (e.g. when running realtime i got error of nulls, but if running using breakpoints, I got result sometime)
Finally found the solution after finding out that execution of script takes more time and variable was null. So added wait function to retrieve once its filled - window.canvasImgContentDecoded
public byte[] TakeScreenshot(string fileName)
{
this.logger.Debug($"Taking screenshot");
var seleniumDownloadPath = this.seleniumEngine.GetDownloadPath();
IJavaScriptExecutor js = Driver as IJavaScriptExecutor;
var html2canvasJs = System.IO.File.ReadAllText(Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), this.seleniumEngine.GetHtml2CanvasPath()));
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(60));
var response = new object { };
js.ExecuteScript(html2canvasJs);
string generateScreenshotJS =
#"
var canvasImgContentDecoded;
function genScreenshot () {
html2canvas(document.body).then(function(canvas) {
window.canvasImgContentDecoded = canvas.toDataURL(""image/png"");
console.log(window.canvasImgContentDecoded);
});
}
genScreenshot();";
response = js.ExecuteScript(generateScreenshotJS);
string getSCreenShot = "return window.canvasImgContentDecoded;";
var encodedPngContent = new object { };
/*ADDED WAIT FUNCTION*/
wait.Until(
wd =>
{
encodedPngContent = js.ExecuteScript(getSCreenShot);
if (encodedPngContent != null)
{
return true;
}
return false;
});
string pngContent = encodedPngContent.ToString();
pngContent = pngContent.Replace("data:image/png;base64,", string.Empty);
string fileSavePath = this.seleniumEngine.GetDownloadPath() + fileName;
File.WriteAllBytes(fileSavePath, Convert.FromBase64String(pngContent));
byte[] fileByte = System.IO.File.ReadAllBytes(fileSavePath);
File.Delete(fileSavePath);
return fileByte;
}

When I return a FileContentResult from Controller.File, the download stalls at 128kb

My controller is returning File(res.File, "application/octet-stream", res.Filename); where res.File is an array of bytes.
I am running this controller using Kestrel, and this works fine for files are are under 128kb in size. Any larger, however, and the download of the file from the api simply stalls out at 128kb.
I'm clearly missing something. I can't find any reference to a limit for this kind of stuff.
I tried decorating the controller method with [DisableRequestSizeLimit], but oddly enough this made it start choking at 64kb.
I'm running dotnet 5.
Controller method
[HttpGet]
[Route("api/v1/reports/get-report")]
public FileResult DownloadReport(
string system,
string environment,
string company,
string quoteNumber,
string template,
string user)
{
var req = new GetQuoteReportRequest()
{
System = system,
Environment = environment,
Company = company,
QuoteNumber = quoteNumber,
Template = template,
User = user
};
return GenerateReportFile(req);
}
private FileResult GenerateReportFile(GetQuoteReportRequest req)
{
var res = _quoteReportService.GetQuoteReport(req);
return File(res.File, "application/octet-stream", res.Filename);
}
Service method
public GetQuoteReportResponse GetQuoteReport(GetQuoteReportRequest req)
{
var message = $"Generate {req.Template} report for quote {req.QuoteNumber}";
_logger.LogInformation($"START: {message}");
var res = new GetQuoteReportResponse();
var time = _timeService.Now;
var excelFilePath = GetExcelFilePath(time, req.QuoteNumber);
var quote = _dataLoader.GetQuote(req, IncludeInternalQuoteDetailLines);
using var workbook = new XLWorkbook();
var worksheet = workbook.Worksheets.Add(req.QuoteNumber);
Builder.BuildReport(worksheet,
quote,
req);
workbook.SaveAs(excelFilePath);
if (IsPdf)
{
var pdfFilePath = CreatePdf(excelFilePath);
res.Filename = Path.GetFileName(pdfFilePath);
res.File = File.ReadAllBytes(pdfFilePath);
}
else
{
res.File = File.ReadAllBytes(excelFilePath);
res.Filename = Path.GetFileName(excelFilePath);
}
_utilities.SendDbEntry(time,
Path.GetFileName(res.Filename),
quote.QuoteLines.Count,
req);
_logger.LogInformation($"END: {message}");
return res;
}

Get environment variables in .Net Core / UWP

I try to migrate our .Net 4 desktop application to an UWP App, which is a hard task without documentation like MSDN. Know I have all methods to connect to the existing Webservice, but the used methods return nothing.
var envVars = Environment.GetEnvironmentVariables();
var host = Environment.GetEnvironmentVariable("COMPUTERNAME");
var user = Environment.GetEnvironmentVariable("USERNAME");
var userDnsDomain = Environment.GetEnvironmentVariable("USERDNSDOMAIN");
GetEnvironmentVariables has a count of 0 and the old methods from .Net don't exist anymore:
var host = System.Net.Dns.GetHostName();
var user = Environment.UserName;
I need the name of the computer, the logged in username and the domain.
Is there a trick or an alternative?
My solution:
var users = await User.FindAllAsync(UserType.LocalUser);
var user = (string) await users.FirstOrDefault().GetPropertyAsync(KnownUserProperties.AccountName);
var domain = "";
var host = "";
if (string.IsNullOrEmpty(user))
{
var domainWithUser = (string) await users.FirstOrDefault().GetPropertyAsync(KnownUserProperties.DomainName);
domain = domainWithUser.Split('\\')[0];
user = domainWithUser.Split('\\')[1];
}
var host = NetworkInformation.GetHostNames().First(x => x.Type == HostNameType.DomainName).DisplayName.Split('.')[0];
This answer is more of a warning.
You cannot query environment variables ad-hoc in UWP, not even with PInvoke
You can query the user's information, but there's absolutely no guarantee the User API will work for multiple user-types, so you need to defensively code against null values. I have no idea why this API is so unreliable.
Here's a sample MainPage and a screenshot showing all the data returned as blank.
using System;
using System.Linq;
using Windows.System;
using Windows.UI.Xaml.Controls;
namespace App_1
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
GetUserInfo();
}
public static async void GetUserInfo()
{
var users = await User.FindAllAsync(UserType.LocalUser);
var user = await users.FirstOrDefault().GetPropertyAsync(KnownUserProperties.AccountName);
var user_count = users.Count;
var AccountName =
await users[0].GetPropertyAsync(KnownUserProperties.AccountName);
var DisplayName =
await users[0].GetPropertyAsync(KnownUserProperties.DisplayName);
var DomainName =
await users[0].GetPropertyAsync(KnownUserProperties.DomainName);
var FirstName =
await users[0].GetPropertyAsync(KnownUserProperties.FirstName);
var GuestHost =
await users[0].GetPropertyAsync(KnownUserProperties.GuestHost);
var LastName =
await users[0].GetPropertyAsync(KnownUserProperties.LastName);
var PrincipalName =
await users[0].GetPropertyAsync(KnownUserProperties.PrincipalName);
var ProviderName =
await users[0].GetPropertyAsync(KnownUserProperties.ProviderName);
var SessionInitiationProtocolUri =
await users[0].GetPropertyAsync(KnownUserProperties.SessionInitiationProtocolUri);
var User_Type = users[0].Type;
}
}
}

Load text from web during xml parsing in Windows 8 Async

I have a unique issue, I want to get the name of an application from it's AppID while I convert an XML file into objects. This is the code I'm using at present:
if (xdoc.Element("Application") != null)
{
var data = from query in xdoc.Descendants("AppID")
select new APP
{
AppID = query.Value,
AppName = GetName(query.Value).ToString(),
};
itemGridView.DataContext = data;
}
This is the code I'm using to convert the GUID into an app name using Microsoft's Store API. I can confirm that it does return the app name. I'm just unsure how I can get this to display.
private async Task<string> GetName(string guid)
{
try
{
string link = "https://services.apps.microsoft.com/browse/6.2.9200-1/615/en-NZ_en-NZ/c/NZ/cp/10005001/Apps/{0}";
string url = string.Format(link, guid);
var httpClient = new HttpClient();
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.SendAsync(httpRequestMessage);
var xmlString = await response.Content.ReadAsStringAsync();
XmlDocument NameXML = new XmlDocument();
NameXML = await XmlDocument.LoadFromUriAsync(new Uri(url));
string sAppName = NameXML.GetElementsByTagName("T")[0].ChildNodes[0].NodeValue.ToString();
return sAppName;
}
catch(Exception)
{
return guid;
}
}
I think my problem is with the async / await tasks. I've just been exposed to it now... how would I load up the App Name alongside the AppID when I parse the xml file?
The output that's being displayed when I run the app is "System.Threading.Tasks.Task[System.String]" (The objects load and the links and everything works fine, its just that the above is displayed instead of the app name).
I've been debugging using breakpoints, it appears that the GetName method only seems to be triggered later on, I'm not sure however.
Try to change this line :
AppName = GetName(query.Value).ToString(),
To this :
AppName = await GetName(query.Value),
GetName will return Task<string> instead of string when not awaited. And the method where above code resides required to be async because of using await inside that method :
private async void SomeMethod()
{
....
if (xdoc.Element("Application") != null)
{
var data = from query in xdoc.Descendants("AppID")
select new APP
{
AppID = query.Value,
AppName = await GetName(query.Value),
};
itemGridView.DataContext = data;
}
....
}
UPDATE :
As you already noticed, LINQ has very limited support for async/await currently. So to workaround this limitation, we can use normal for loop to avoid calling async function inside LINQ :
private async void SomeMethod()
{
....
if (xdoc.Element("Application") != null)
{
var query = from query in xdoc.Descendants("AppID")
select query.Value;
var data = new List<App>();
foreach (var q in query)
{
data.Add(new App{ AppId = q, AppName = await GetName(q) });
}
itemGridView.DataContext = data;
}
....
}

Categories

Resources