Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

why is apply_update_at needed? #3

Closed
riverKanies opened this issue Nov 13, 2024 · 19 comments
Closed

why is apply_update_at needed? #3

riverKanies opened this issue Nov 13, 2024 · 19 comments

Comments

@riverKanies
Copy link
Contributor

riverKanies commented Nov 13, 2024

I would really love to understand better why this is needed. do you have insight on this @darioAnongba ? where did you find this solution? I spent several hrs today trying to resolve the errors caused by not having this, they were very cryptic and I wasn't able to find any hint that this might be a solution. it seems to be a wasm specific issue, so whatever the underlying reason is I wouldn't be surprised to see it appear again elsewhere in the future (referring to this line of code 9a393b9#r149057121)

I'd say this is a critical topic to cover in bdk wasm documentation

@riverKanies
Copy link
Contributor Author

running ‘wasm-pack test --headless --safari’ with apply_update gives

'time not implemented on this platform'

so it seems 'time not implemented on this platform' is the issue solved with apply_update_at, were using a custom timestamp because the wasm environment doesn't support the default

In a normal Rust environment, .apply_update() would internally call std::time::SystemTime::now() to get the current timestamp. However, in WebAssembly (particularly in the browser), direct system time access isn't available - hence the error "time not implemented on this platform".

The solution you implemented:

let now = (Date::now() / 1000.0) as u64; // Get time from JavaScript
wallet.borrow_mut()
.apply_update_at(update, Some(now)) // Explicitly provide the timestamp

This works because:

Instead of relying on Rust's SystemTime, you're using JavaScript's Date.now() (accessed through js_sys::Date)

Date.now() returns milliseconds since Unix epoch, so you divide by 1000 to get seconds

.apply_update_at() allows you to explicitly provide this timestamp instead of trying to get it internally

This is a common pattern when working with WebAssembly - when certain system-level features aren't available, you often need to bridge the gap by getting that functionality from the JavaScript environment instead.

(p.s. sadly I was stuck on some other issue so I never got the 'time not supported' hint. makes sense tho!)

@riverKanies
Copy link
Contributor Author

interesting that this is an issue with time but not network, seems bdk_esplora has built in access to js bindings when in wasm, but bdk_wallet doesn't have built in access to js time in wasm for some reason...?

@darioAnongba
Copy link
Contributor

darioAnongba commented Nov 14, 2024

I was going to answer but it seems you figured it out. Yes using std in Rust doesn't work with WASM (wasm32-unknown-unknown) because std relies on the standard library specific to the OS, whereas this target runs on the browser. Other WASM targets such as wasm32-wasi can access OS stuff like file system, sockets, time, etc.

The reason other packages work is because we are replacing some dependencies like ring and getrandom, etc. to target the correct env by setting features.

In the browser, I used Date to get the current timestamp in seconds but there may be a better way.

Understanding all this stuff requires deep knowledge of compilers on different OSs and their standard libraries, which is a pain and should not be needed in an ideal world where you just want to use bdk, that's why I want to build a bdk-wasm package so people can simply do yarn add bdk-wasm.

@riverKanies
Copy link
Contributor Author

@darioAnongba I think that's a great idea, I've also been curious about what an ffi for JS might look like, would it just be an easier way to use bdk in js envs? or would it only work server side?

so is this system issue similar to why only esplora works in wasm? like the explora package is built to look for the js fetch and use if available, but the electrum and rpc packages don't? that would be another very important thing to document

@riverKanies
Copy link
Contributor Author

also it's curious that when I try to change back to apply_update I get a 'time not implemented on this platform' error message when running tests. this is all I would have needed to figure it out, but it seems that some other change you made enabled this error message. before I was getting no error message beyond "Failed to detect test as having been run. It might have timed out." I was however able to use logs to identify that it was the apply line of code with the issue, just no hint to what it actually was. eventually I would have looked into the limitations of wasm and maybe realized it was a timestamp issue, but that's just such a bad developer experience

@darioAnongba
Copy link
Contributor

I've also been curious about what an ffi for JS might look like, would it just be an easier way to use bdk in js envs? or would it only work server side?

Not an expert but I've never seen FFI for JS (Node). Might not exist or not worth it since it doesn't work on the browser.

so is this system issue similar to why only esplora works in wasm? like the explora package is built to look for the js fetch and use if available, but the electrum and rpc packages don't? that would be another very important thing to document

MIght be multiple things but one of them is that the electrum lcient expects raw socket connections (no idea why it doesn't use normal HTTPS): bitcoindevkit/bdk#843. Pretty sure that if we solved this it would still fail with something else.

@riverKanies
Copy link
Contributor Author

riverKanies commented Nov 14, 2024

so is your plan for this repo to be a fully api to all of bdk functionality? I would assume not (although perhaps the ffi bindings could somehow be leveraged to do so...?). I guess the alternative would be a minimal feature set of bdk that people can then fork for more advanced functions? just curious if you have a specific vision of what this should be @darioAnongba
I'd say it should at the least be an example to demonstrate the wasm limitations (that will be discussed in the docs)

@darioAnongba
Copy link
Contributor

Sorry I'm slow to answer. For the moment I don't have a plan to create a bdk-js (using WASM) package that I would maintain myself but my company would certainly be interested in doing so. So at the moment I'm working on this as a PoC and proving that it will work for browser and browser extensions. If it does, it will be adopted by my company and others like Proton. Main issue for us is that we can't compile BDK with the Electrum and JSON-RPC clients and are forced to use Esplora.

In summary, I expect that if everything goes well, this repo will be moved to the org and maintained as open source software.

although perhaps the ffi bindings could somehow be leveraged to do so...?

If what you need is a Node package, you will most likely be able to do so by using https://github.com/neon-bindings/neon. But if what you need is to run things in the browser, WASM is your best bet.

@riverKanies
Copy link
Contributor Author

I'm speculating about the possibility of leveraging the ffi setup to produce wasm bindings. the idea would be to make the wasm lib the same api as ffi for other languages (with some limitations ofcourse) and to minimize maintanance of a wasm lib by being able to auto generate new wasm bindings for bdk updates. it wouldn't use uniffi, but would leverage the parsing aspect of that to generate wasm bindings instead of ffi... does that make sense?

put a different way, it seems like the bdk-wasm api should be very similar to the bdk-ffi libs, so I'm thinking about trying to add an automated script to the bdk-ffi lib to convert that rust to wasm bindings. and then I was thinking we could have a list of functions that won't work or need special implementation which would be the only part that needs maintenance.

I was just wondering if you think this seems doable and worth investing time in? or is there some reason this won't work? @darioAnongba

@darioAnongba
Copy link
Contributor

TBH I'm not familiar enough with FFI to be able to tell if that can work. I don't see how to do this part:

so I'm thinking about trying to add an automated script to the bdk-ffi lib to convert that rust to wasm bindings

Would be great if your idea worked and it you think you can pull it out I'm happy to help. maybe some kind of simple example?

@riverKanies
Copy link
Contributor Author

ya, I'm thinking I'd start by manually converting some of the FFI api to wasm, and then I think it'd be more clear at that point what would make the most sense. Thunder was saying that the FFI api is somewhat limited, so there are things you can do in wasm that you actually can't through FFI. But when I think about what bindings we want in a bdk-wasm lib it seems like the FFI api would likely be pretty close to ideal. I'll play around with this idea this week and let you know how it goes

@riverKanies
Copy link
Contributor Author

@darioAnongba I've started working on a WASM page for the book of bdk, lmk if you think of info that would be good to add bitcoindevkit/book-of-bdk#85

@riverKanies
Copy link
Contributor Author

here is my progress with the ffi => wasm idea
bitcoindevkit/bdk-ffi#631
I'm sure this could be leveraged for some sort of automation, but unclear how far down the rabbit hole to go. I'm thinking that at minimum it could be a way to compare the ffi api to the wasm api. It seems to me like it would make sense for the bdk-wasm api to mirror the ffi api, and so this could be a validation of that.
Another potentially low hanging fruit is to generate an outline of the rust code needed for wasm bindings

@darioAnongba
Copy link
Contributor

I didn't know you were using my repo in the BDK book. I will be more careful in the future not to break stuff and keep it in a clean state (at least a specific tag).

Regarding the FFI stuff, I have no experience with it but happy to help test it in bdk-wasm (that should maybe be renamed bdk-js).

@riverKanies
Copy link
Contributor Author

I threw a copy of your bdk-wasm repo into the book to grab code snippets from, I'll probably trim that down to a minimal wasm example, but ultimately it would be nice to recommend the bdk-wasm lib if it has good coverage of functionality. Not totally sure which direction makes the most sense, but feel free to develop on your bdk-wasm lib as you see fit, I just wanted to get something in the book to get a conversation started

@riverKanies
Copy link
Contributor Author

@darioAnongba sorry this has become my default thread for communicating with you. are you in the bdk discord?

I'd like to consult you on what I'm attempting to do. As I mentioned I'm hoping to be able to make a bdk-ffi-wasm lib that would mirror as close as possible the api for ffi libs. I made a new lib to try, and heres the branch where I'm having an issue https://github.com/riverKanies/bdk-ffi-wasm/tree/add-esplora
basically my current understanding is that anything passed through a wasm binding needs to be serializable or in other words convertible to/from a JsValue. is that correct or am I missing something here? anyway, in my attempt to make types serializable I've been going down a rabbit hole where it seems I would need to make proxy types for bdk_core types to make them serializable. If that is the case then the idea may not make sense unless bdk were to make all types serializable.
perhaps addressing this issue is a big part of what the bdk-ffi lib (uniffi) does.
my hope was to be able to convert the rust code in the bdk-ffi repo into wasm bindings with minimal effort. It seems to be getting messy when trying to pass the request/update values through bindings. I'm still very much trying to wrap my head around how to pass values through bindings.

as far as the progress I have made, you can see on the main branch of that repo that I was able to get a minimal implementation of Descriptor working. That was relatively easy as it only interfaces with strings

@riverKanies
Copy link
Contributor Author

it seems I may have been trying to utilize JsValue when the best approach would be to follow the wrapper pattern you have. I've switched to focusing on that and seem to be making progress. (see main branch). I've managed to add the esplora client and wallet code from bdk-ffi and made necessary changes to compile (remove rusqlite and related stuff mainly). At this point it is quite a bit of glue code (that is, I've had to make significant changes to the ffi rust code to make wasm_bindings) but I do still think it may ultimately be worth while to have the bdk-wasm lib mirror the ffi api 90%. It should theoretically make it easier to maintain aswell I think.
on the topic of ffi -> wasm automation: the amount of glue code and special wasm specific cases I think makes this less interesting than it originally seemed. It may still be worth some sort of api comparison (ffi to wasm bindings), but that's probably not worth the effort

@riverKanies
Copy link
Contributor Author

I've been having a surprising amount of difficulty with navigating the options for wasm interface. but I've more or less just copied the wrapper pattern you have on the wallet for some other types to try to match the ffi api and it is working. still have some naming cleanup to work out, I think it'd be nice to hide all the wrappers and make it look like you're just calling the functions directly like in ffi.

@darioAnongba
Copy link
Contributor

Answered on Discord.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants