Skip to content

Commit a62f398

Browse files
committed
context: add methods to provide immutable access and rerandomization
This covers both the std and no-std cases. This is perhaps an unconventional strategy; see rust-bitcoin#346 for discussion leading up to it.
1 parent 7d925f6 commit a62f398

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

src/context.rs

+92
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,98 @@ use crate::ffi::types::{c_uint, c_void, AlignedType};
88
use crate::ffi::{self, CPtr};
99
use crate::{Error, Secp256k1};
1010

11+
// This module will be renamed to `global` in later commit which eliminates the other `global` module
12+
#[cfg(feature = "std")]
13+
pub mod global_ {
14+
use core::cell::RefCell;
15+
use crate::{ffi, All, Secp256k1};
16+
17+
thread_local! {
18+
pub static CONTEXT: RefCell<Secp256k1<All>> = RefCell::new(Secp256k1::new());
19+
}
20+
21+
/// Do some operation using an immutable reference to the global signing context
22+
///
23+
/// Safety: you are being given a raw pointer. Do not mutate through it. Do not
24+
/// move it, or any reference to it, across thread boundaries.
25+
pub unsafe fn with_global_context<F: FnOnce(*const ffi::Context) -> R, R>(f: F) -> R {
26+
CONTEXT.with(|refcell| f(refcell.borrow().ctx as *const _))
27+
}
28+
29+
/// Rerandomize the global signing context
30+
///
31+
/// # Panics
32+
///
33+
/// Will panic if called from a closure passed to `with_global_ctx`.
34+
pub fn rerandomize_context(seed: &[u8; 32]) {
35+
CONTEXT.with(|refcell| {
36+
let borrow = refcell.borrow_mut();
37+
let bare = borrow.ctx;
38+
unsafe {
39+
let ret = ffi::secp256k1_context_randomize(bare, seed.as_ptr());
40+
assert_eq!(ret, 1); // sanity check: context_randomize cannot fail
41+
}
42+
});
43+
}
44+
}
45+
46+
#[cfg(not(feature = "std"))]
47+
mod global_ {
48+
use core::sync::atomic::{AtomicBool, Ordering};
49+
use crate::ffi;
50+
51+
static LOCK: AtomicBool = AtomicBool::new(false);
52+
53+
// Safety: all of the unsafe blocks in this module are due to our accessing
54+
// extern statics (the ffi::* context objects). Rust cannot control access
55+
// to these, so we need unsafe.
56+
//
57+
// In particular, we are promising here that nothing else will concurrently
58+
// access ffi::secp256k1_context_signing_{1, 2} ... though in principle,
59+
// any code that can see the `ffi` module will be able to do so. We can only
60+
// make promises about our own code.
61+
//
62+
// Other accessors, who to be clear SHOULD NOT EXIST, will need their own
63+
// unsafe block. As far as I know this is the best we can do.
64+
65+
/// Do some operation using an immutable reference to the global signing context
66+
pub unsafe fn with_global_ctx<F: FnOnce(*const ffi::Context) -> R, R>(f: F) -> R {
67+
let ctx = unsafe { ffi::secp256k1_context_signing_1.load(Ordering::Acquire) };
68+
f(ctx as *const _)
69+
}
70+
71+
/// Do some operation using a mutable reference to the global signing context
72+
pub fn rerandomize_context(seed: &[u8; 32]) {
73+
if LOCK.swap(true, Ordering::Acquire) {
74+
// Concurrent operation in progress; give up
75+
return;
76+
}
77+
78+
unsafe {
79+
// Obtain a pointer to the alternate context.
80+
let alt_ctx = ffi::secp256k1_context_signing_2.load(Ordering::Acquire);
81+
// Obtain a pointer to the signing context, atomically replacing it with
82+
// the alternate signing context (which never gets modified). Any concurrent
83+
// callers to `with_global_ctx` will see the alternate context, not any
84+
// inconsistent state of this one.
85+
//
86+
// (Concurrent callers to `with_global_ctx_mut` will fail after checking `LOCK`.).
87+
let ctx = ffi::secp256k1_context_signing_1.swap(alt_ctx, Ordering::Acquire);
88+
// Do the rerandomization. Note that unlike in the `std` case, we don't
89+
// panic if this function fails (which again, it is guaranteed not to).
90+
// The reason is that a panic at this point in the code would leave
91+
// `LOCK` at true (as well as both `signing_1` and `signing_2` pointing
92+
// at the same, never-rerandomized, place) and therefore prevent any
93+
// future rerandomizations from succeeding.
94+
ffi::secp256k1_context_randomize(ctx, seed.as_ptr());
95+
// Atomically swap completely-modified context into place
96+
ffi::secp256k1_context_signing_1.swap(ctx, Ordering::Release);
97+
};
98+
99+
LOCK.store(false, Ordering::Release);
100+
}
101+
}
102+
11103
#[cfg(all(feature = "global-context", feature = "std"))]
12104
#[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "std"))))]
13105
/// Module implementing a singleton pattern for a global `Secp256k1` context.

0 commit comments

Comments
 (0)