-
Notifications
You must be signed in to change notification settings - Fork 281
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
Implementing pre allocation context creation #116
Conversation
I hoped I could leverage (tried combining traits: https://play.rust-lang.org/?gist=1c23c763ea2438eb620f51fbfb985515, |
Ok, I thought about it and I think the best way to do this is by testing how much memory each flag requires and then putting that on the stack and panic if the result of (that way we can preserve the API) What do you guys think? @apoelstra |
Hm, the problem with that approach is that the size of the underlying context depends on the (C) platform for which secp256k1 has been compiled and the configuration settings (endo, etc.). Given what's currently in the context, we could reasonably guess an upper bound depending on configuration settings, etc. But that seems overly complicated. What's the downside of adding Is the underlying problem that you want to avoid the heap for |
Yes. The underlying problem is to avoid heap allocation and give a fully no-std support. In the current implementation I wrote there's a I just thought that it would be nicer to preserve API if we can put an upper bound and then put it on the stack, but if it's platform specific then it'll be overly complicated as you said. |
Hm I see. Offering Maybe we can convince upstream to make |
yeah, so the user might have a big scratch space available (either heap or stack) which he could use, or he might have the ability to do some weird allocations himself(see #119 for example) but it would be easier if we could solve it on the stack for the user. if the whole size thing could've been constant it would be nice but I think there's some implementation difficulties and they're hoping to just make the Context itself constant and then they could get rid of it completely(and replace it with compilation flags for the size of the context) |
For the |
@apoelstra So you're suggesting to implement the trait directly on the structs/enums and not through the already existing traits, that's too bad but I guess that's all we can do. Maybe i'll make another PR that replaces the structs with enums and adds this trait, I think it should be in a separate PR |
Yeah, it kinda sucks but I think it's the correct thing to do actually - you can imagine some future context type has different flags but still implements the ordinary |
OTOH this would assumes that we have enough stack space and the user wants to do this on the stack. One typical use of |
Hi! @real-or-random - given latest changes and my yesterday's "research" 😄 I had impression that all I need is to define lower 'ECMULT_WINDOW_SIZE' for static precompute tables. So I set in build.rs:58 Then I even refreshed secp256k1 depend sources with latest and even tried to merge#337 -variable sized precomputed table for signing (where you are also reviewer) on top. That's after both #115 and #116. And everywhere behavior is virtually the same - SEGFAULT at Here is the diff between this pr and my experimental branch |
@real-or-random -oh btw - in build.rs there is line |
@vhnatyk Hm weird, can you open a separate issue with more background?
The |
I admit that it's a bit of black magik, but my current understanding of the subject doesn't let me generate any more useful info, unfortunately 😊 |
build.rs
Outdated
.define("ENABLE_MODULE_ECDH", Some("1")); | ||
.define("ENABLE_MODULE_ECDH", Some("1")) | ||
.define("USE_EXTERNAL_DEFAULT_CALLBACKS", Some("1")) | ||
.define("ECMULT_WINDOW_SIZE", Some("15")); // This is the default in the configure file (`auto`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ECmult window size needs to be configurable, 15 is definitely too large for smaller platforms
(though I don't know if rust has any way to pass values instead of feature bits?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there is a way in rust to pass variables as compile time arguments. but i'll look into it, maybe there's some hack we can use (maybe environment variable?)
So after talking with apoelstra, it seems like the right way would be to split this PR to 2.
I'll implement that after #115 will get merged. |
ctx: *mut ffi::Context, | ||
phantom: PhantomData<C> | ||
phantom: PhantomData<C>, | ||
_buf: Option<&'buf [u8]>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think unconditionally adding 'buf
to the public API will make things too unergonomic for users who aren't providing their own buffer.
I think it'd be better to add a new set of context types SignOnlyBuffer<'buf>
VerifyOnlyBuffer<'buf>
AllBuffer<'buf>
which tie the lifetime of the passed-in buffer to the context. Then let phantom
deal with the borrowck and change the _buf
field into buf: Option<*mut u8>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm you prefer that no-std
users will have a different Context struct? (If you provide no buffer you shouldn't feel the new lifetime at all(as a user, for the library maintainers you're right that it's unergonomic))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do go in that direction we'll need to abstract out the Context, so that sign/verify functions won't be implemented on the struct itself but on a generic of the trait (adding something like get_ptr()
to the traits)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the nice thing about having &[u8]
is that it's a fat pointer which makes it easier to clone (no need to recheck the size)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the very least having 'buf
in the API makes the documentation confusing, since users will see a lifetime but have no idea what it's for or how to use it.
I'm pretty sure sign/verify are already abstracted over the context, since e.g. both SignOnly
and All
have signing functionality.
Secp256k1 { | ||
ctx: unsafe { ffi::secp256k1_context_clone(self.ctx) }, | ||
phantom: self.phantom | ||
phantom: self.phantom, | ||
_buf: None, | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...then I'd implement Clone
only for the non-Buffer
versions of Secp256k1
.
pub fn new() -> Secp256k1<All> { | ||
Secp256k1 { ctx: unsafe { ffi::secp256k1_context_create(ffi::SECP256K1_START_SIGN | ffi::SECP256K1_START_VERIFY) }, phantom: PhantomData } | ||
pub fn new() -> Secp256k1<'buf, All> { | ||
Secp256k1 { ctx: unsafe { ffi::secp256k1_context_create(Self::FLAGS) }, phantom: PhantomData, _buf: None } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed on IRC, I'd like to allocate our own memory in Rust and then call the prealloc functions.
So A couple of design decisions we need to make:
If we move the flags into a trait, and create a different struct for |
I don't understand what you mean by "move the flags into a trait". Can you be more explicit? Also while you're messing with structs can you make them all empty enums? (#117) |
Closed in favor of #125 |
Hi,
I want to implement bitcoin-core/secp256k1#566
this PR build on top of #115.
I see 2 ways to do it:
expose
get_preallocated_size()
,new_preallocated_context()
functions to the user and add the reference to that buffer into theContext
struct so rust won't allow him to write into it anymore.just assume that if he can allocate data on the fly then he will Implement the https://doc.rust-lang.org/beta/std/alloc/trait.GlobalAlloc.html and just use liballoc instead. (and allocate ourself with Vec).
This is a first try on how 1 will look like (didn't write function docs and I hope I can find better function names haha)