MetacoSA / QBitNinja

An Open Source and powerful blockchain API
MIT License
68 stars 42 forks source link

QbitNinja and NBitcoin Plugin for iOS #120

Closed andygruening closed 3 years ago

andygruening commented 4 years ago

Hey, so I made an mobile App using the Nbitcoin and qbitninja plugins with Unity. The Android version is working, but I noticed that it only worked with having the scripting backend set to Mono, with IL2CPP it didnt work. So I have the problem now, that iOS ARM64 architecture only supports IL2CPP and when I build it, it doesnt work as well. Is that something known or were these plugins never intended to work on iOS?

Thanks in Advance!

andygruening commented 4 years ago

In addition, here the error warning:

I/Unity ( 1996): ValidatePrivateKey::ERROR[One or more errors occurred.]========== at System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) [0x00000] in <00000000000000000000000000000000>:0 I/Unity ( 1996): at System.Threading.Tasks.Task1[TResult].GetResultCore (System.Boolean waitCompletionNotification) [0x00000] in <00000000000000000000000000000000>:0 I/Unity ( 1996): at YourBitcoinController.BitCoinController.GetUnspentCoins (NBitcoin.BitcoinAddress _address, System.Boolean _dispatchEvent) [0x00000] in <00000000000000000000000000000000>:0 I/Unity ( 1996): at YourBitcoinController.BitCoinController.GetBalance (System.String _privateKey, System.Boolean _dispatchEvent) [0x00000] in <00000000000000000000000000000000>:0 I/Unity ( 1996): at YourBitcoinController.BitCoinController.ValidatePrivateKey (System.String _encryptedKey) [0x00000] in <00000000000000000000000000000000>:0 I/Unity ( 1996): at YourBitcoinManager.ScreenBitcoinPrivateKeyView.UpdateValidationVisualizationKey (System.String _privateKey) [0x00000] in <00000000000000000000000000000000>:0 I/Unity ( 1996):`

ArtjomP commented 4 years ago

We use NBitcoin & QBitNinja in our cross-platform wallet pt.BTC built with Xamarin.Forms . It compiles & works on iOS (ARM64) without any problem.

NicolasDorier commented 4 years ago

I think it should work with iOS. If you can get more info about what crash would be cool. Maybe try just copying the client code in your code.

andygruening commented 4 years ago

Hey, so I have been testing more and more from time to time and I didnt get to it work still. The main thing I noticed that this is not only the problem with iOS but also with Android when having the scripting backend set to IL2CPP. I was using Unity version 2019.2.8 . I said in my original post it was working, because I tested it with Mono on Android. So it seems like a IL2CPP thing. I was thinking, maybe the QBitNinja dll is not supported for IL2CPP in Unity as in some cases you have to go through some workarounds to make outside c# dlls work in Untiy. I am using the dll from NugetPackageManager, latest version. Im pretty sure thats no the case because I can make an Instance of QbitNinjaClient, which is a part of that dll, without any exception. Which looks like that:

QBitNinjaClient clientQBitNinja = new QBitNinjaClient("http://api.qbit.ninja/", NBitcoin.Network.Main);

The error happens at this step, when I am trying to get the balance:

var balanceModel = clientQBitNinja.GetBalance(_address, true).Result; (The '_address' is a BitcoinAddress Instance from Nbitcoin)

which gives the following error as mentioned in my original post: System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) [0x00000] in <00000000000000000000000000000000>:0 I/Unity ( 1996): at System.Threading.Tasks.Task1[TResult].GetResultCore (System.Boolean waitCompletionNotification) [0x00000] in <00000000000000000000000000000000>:0

I already got those errors, in other projects, when there was an issue with a Http Request. As 'GetBalance' will obviously make a http request, there might be my issue, somehow related to IL2CPP. The method this is coming from is from the QBitNinjaClient dll. Do you guys have any clue what this might be? I would really appreciate it, thanks!

andygruening commented 4 years ago

I have also just tested if the '_address' I am giving the GetBalance method is null, which isnt the case. I am also getting the Task back from GetBalance(), but the error occurs when I try to access the Result of that Task. And it just works fine when Im not building it with IL2CPP

NicolasDorier commented 4 years ago

@andygruening I have no idea why that would happen. Did you picked the right assembly?

I don't know how unity work but there is several assembly in the nuget package https://www.nuget.org/packages/QBitNinja.Client/1.0.3.52

You should try .NETStandard 1.1 or .NETStandard 2.1

Else, you can try to copy/paste the code of the client class in your own code, see how it goes.

andygruening commented 4 years ago

Alright thanks! I will try that later and will let you know if that worked

NicolasDorier commented 4 years ago

any news?

andygruening commented 3 years ago

Hey, sorry for the later update, didn't have so much time lately.

I tried all the different assemblies and they all caused the same problems unfortunately.

So I was looking more into it, I was thinking maybe there is some incompatibility with the System.Net.Http library and IL2CPP on Unity, as the QBitNinjaClient seem to use that Library for making the calls and the error clearly appears there. So after updating to the latest Unity LTS version, it didn't solve it. I was also making other projects to test the Http Library with IL2CPP but it worked fine for the functions that I tested. I also noticed that the error appears one second after making the http call. So the Library seems to work and the error appears after it retrieved the data from some server.

So after that, I am assuming it might be some deserialization problem? Because the error is appearing at this line: var balanceModel = clientQBitNinja.GetBalance(_address, true).Result; and specifically, the error is appearing when calling the ".Result" which should deserialize the retrieved server data to a BalanceModel object, right?

When Im calling GetBalance() without ".Result" its not null and gives me the Task respectively

So is there a possibility that the data can be corrupt and couldn't deserialize? But then why would it not be corrupt without IL2CPP. So I am pretty sure now, that this is rather a Unity problem.

NicolasDorier commented 3 years ago

you can't use await to unwrap the task in unity il2cpp?

andygruening commented 3 years ago

Yes I tried that, but it didn't have an effect. But I was able to finally solve it the other day. It was because of code stripping in Unity. The BalanceModel class from QBitNinjaClient has an empty constructor, so Unity stripped that out in the actual build and Json.Net failed to deserialize it. Luckily Unity has a system where you can Ignore a library from code stripping. After doing that, it worked on Android and iOS with IL2CPP

NicolasDorier commented 3 years ago

Thanks for reporting that!