-
Notifications
You must be signed in to change notification settings - Fork 19
Simplicity bindings #22
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
Simplicity bindings #22
Conversation
|
Please check if this is the way we should expose C bindings, @apoelstra, @sanket1729. I mimicked secp256k1-sys with important differences:
|
| .into_iter() | ||
| .map(|x| simplicity_path.join(x)) | ||
| .collect(); | ||
| let test_files: Vec<_> = vec![ |
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.
In cdee8cb:
I don't think we need this array (or these files, for that matter).
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.
Ah -- you have unit tests in -sys that use them. In this case I think you could gate this array with #[cfg(test)] so that it's not built when simplicity-sys is used as a dependency.
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.
We could reduce the built files to a minimum. For instance, in order to construct C-DAGs from binary, we definitely need decodeMallocDag() from dag.c.
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 point of this PR is to expose (1) a way to decode DAGs from bytes in C, and (2) test programs with hardcoded bytes and Merkle roots. This is why we need test_files. My original plan was to test Rust against C, but this has to be done in a future PR due to outdated jets.
simplicity-sys/src/bitstream.rs
Outdated
| } | ||
|
|
||
| impl From<&[u8]> for Bitstream { | ||
| fn from(bytes: &[u8]) -> Self { |
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.
In 8e45d76:
This entire function needs to be marked unsafe, or better, you need to tie the lifetime of bytes to the Bitstream type (perhaps with one of the types in std::marker). As written it takes an array bytes and then returns an object pointing into bytes' data which might outlive bytes.
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 updated Bitstream to Bitstream<'a> where 'a is the lifetime of &bytes.
simplicity-sys/src/dag.rs
Outdated
| jet: *const c_void, | ||
| cmr: [u32; 8], | ||
| // Assume largest union member | ||
| union_aux_types: [size_t; 2], |
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.
In 8e45d76:
This makes me a little nervous. Rust has a union keyword. Could you use that?
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.
|
Regarding "should we try to minimize the set of C", I vote no. The whole C library is super small; better to use the whole thing to make review easier. Link-time optimization will strip unused symbols from the final binaries. Done reviewing 8e45d76. Overall this looks great! |
simplicity-sys/src/dag.rs
Outdated
| /// Returns the DAG and its length on success. | ||
| pub fn decode(bytes: &[u8]) -> Result<(&mut DagNode, usize), Error> { | ||
| let mut bitstream = Bitstream::from(bytes); | ||
| let mut dag: *mut DagNode = std::ptr::null_mut(); |
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.
Do we have to explicitly free the DAG in the destructor of DagNode? In the C code, the DAG is freed after use.
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.
Yep, you need to free dag in the Drop impl of DagNode. Good catch.
8e45d76 to
213c697
Compare
simplicity-sys/src/bitstream.rs
Outdated
| unsafe { | ||
| let mode = "r"; | ||
| // `file` does not need to be freed as | ||
| // `bytes` will free itself at the end of its lifetime |
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.
In 213c697:
This comment doesn't make sense to me. At some point you do need to fclose file, don't you?
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.
Bitstream<'a> lives at most as long as &'a bytes. According to man pages, fmemopen does not open a file descriptor, nor does a read-only stream have any buffers that need to be flushed. fclose simply closes file descriptors and flushes. If I understand correctly, this means that the FILE pointer in the stream can safely be dropped without freeing it.
That being said, not freeing a FILE pointer is probably bad practice. I will add a destructor.
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'm sure that you need to actually call fclose at some point in this source file.
Otherwise, the lifetime stuff looks like it's been fixed.
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.
Actually, on further reading I don't know. I can't find a citation either way. It is surprising that there is no descriptor and that fileno will just fail.
|
Briefly reviewed 213c697. (Only changes from the last review were in the final commit; I think we'll need a bit more iteration on this one.) |
213c697 to
a0faaf2
Compare
|
I updated how DAGs are represented, and added Because |
|
@apoelstra This should be ready to be merged. Sorry for the many force pushes. |
simplicity-sys/src/dag.rs
Outdated
| len: usize, | ||
| } | ||
|
|
||
| impl<'a> fmt::Debug for Dag { |
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.
In a0faaf2:
nit: this 'a shouldn't be there. I'm not sure why the compiler doesn't warn about this.
|
Could you also rebase onto master? This currently doesn't have #23 which is making it hard for me to test locally. |
a0faaf2 to
190a669
Compare
apoelstra
left a comment
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.
ACK 190a669
sanket1729
left a comment
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.
high level review ACK. Left a couple questions
| } | ||
|
|
||
| /// Simplicity program with bytes, CMR and IMR for testing. | ||
| pub struct TestProgram { |
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.
Where are we testing the decoding works?
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 assume that the values from C are correct (within C's context, using its functions). Testing Rust against C is currently not feasible, due to outdated jets, because these tests will fail.
| fn decode_cmr() { | ||
| let bytes = SCHNORR0.bytes; | ||
| let dag = Dag::decode(bytes).expect("decoding"); | ||
| assert_eq!(SCHNORR0.cmr(), dag.cmr()); |
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.
We should also test that calculation for cmr from SCHNORR0 from rust-simplicity is same as C simplicity. What we are testing here is that is the value exported from C is correctly calculated from C.
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 agree; this was my original intention of this PR. Unfortunately, outdated jets etc. make it so the Rust CMR is almost always different than the C CMR (for the same program). Rust's SCHNORR0 and C's SCHNORR0 are also very different programs, the later using jets and being much smaller. In a later PR, I will replace src/test_progs in Rust with tests that use the FFI once there is a prospect that these tests will succeed.
- sub-crate contains C code - copied from ElementsProject/simplicity/commit/35627fc49ad96fcb844cca72ffbc69b6e934cb4c
190a669 to
66135ce
Compare
apoelstra
left a comment
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.
ACK 66135ce
sanket1729
left a comment
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.
utACK 66135ce. The build script seems fragile. We should have some vendoring-like script that updates the underlying library when anything changes.
|
Let's merge this for now. We can improve things later to write a revendoring script that will do codegen and maybe symbol renaming, but for now this is something we've wanted for a while. |
Exposes part of Simplicity's C API in Rust. At the moment, one can decode Simplicity DAGs from bytes, and one can access test programs with hardcoded bytes and their correct Merkle roots.
Todo
test CMRs and IMRs against C[postponed to later PR due to outdated jets]