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

Rust protobuf API #525

Merged
merged 6 commits into from
Aug 25, 2020
Merged

Rust protobuf API #525

merged 6 commits into from
Aug 25, 2020

Conversation

benma
Copy link
Collaborator

@benma benma commented Jul 2, 2020

This is a working demo of how to use Prost to handle our protobuf api in an async manner.

I am using the prost deps from github as the no_std fixes are not released yet, see https://github.com/danburkert/prost/issues/329. We can discuss if we should move ahead anyway or wait for the release.

bitbox02_rust::hww::api::process corresponds to the C commander().

Still missing: commander_states_can_call() check from commander() - I didn't add it yet to keep the code smaller for review, will add as soon as the concept is approved.

@benma benma requested a review from x1ddos July 2, 2020 09:30
@benma
Copy link
Collaborator Author

benma commented Jul 2, 2020

I know, you don't need to tell me that there are too many vendored files that we don't need :( No idea how to improve that.

Edit: this might be the culprit:

tokio-rs/bytes#400 (comment)

@x1ddos
Copy link
Contributor

x1ddos commented Jul 2, 2020

I know, you don't need to tell me that there are too many vendored files that we don't need :( No idea how to improve that.

Edit: this might be the culprit:

tokio-rs/bytes#400 (comment)

Did you try with a cargo +nightly vendor -Z avoid-dev-deps?

Copy link
Contributor

@x1ddos x1ddos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool! Actually, amazing. A couple items to discuss:

  • would it make sense work on this in a separate "prost" branch?
  • I'd personally prefer to wait for prost release; we're not in a hurry, are we
  • try cargo +nightly vendor -Z avoid-dev-deps or something like that

This is a working demo

It's not really a demo anymore. It does process a valid API call, even though it's just one for now.

src/CMakeLists.txt Outdated Show resolved Hide resolved
tools/prost-build/.gitignore Outdated Show resolved Hide resolved
tools/prost-build/.cargo/config Outdated Show resolved Hide resolved
// limitations under the License.

fn main() {
let messages_dir = std::env::var("MESSAGES_DIR").expect("MESSAGES_DIR env var not set");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use a command line flag?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My issue with env vars is I see this:

    MESSAGES_DIR=${CMAKE_CURRENT_SOURCE_DIR}
    cargo run --manifest-path=${CMAKE_SOURCE_DIR}/tools/prost-build/Cargo.toml

and I have no idea whether it was a mistake, typo or MESSAGES_DIR is really used in the next command.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk, seemed consistent with OUT_DIR imposed by prost-build. And no CLI parsing needed ;)

I will look into it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, that's even more confusing imho:

prost_build::compile_protos(&["hww.proto"], &[&messages_dir]).unwrap();

So, compile_protos takes in what to compile in function arguments, but somehow it wants an env var for the output... that looks very inconsistent, wouldn't you agree?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think OUT_DIR is imposed by the rust build.rs architecture 🤷

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just saying that the reason that prost-build uses OUT_DIR is because cargo sets that environment variable when it runs a build.rs file. You should, on the other hand, use command line arguments to pass input files and output dirs.

I think build.rs files are useful for rust-only projects, but they are kind of evil in that they can do arbitrary things at compile time (like fetching stuff from ze internets). So I try to avoid them. In your case it is better to create a separate tool (like you've done) and run it separately from cargo. This reduces the number of deps on your main project and (when you move it to docker) reduces the overall build time.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm split regarding if the generated sources should be committed. One benefit of committing them is that you notice if you by mistake use a tool that generates new/different output. (Maybe you locally have the wrong version installed for some reason..)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like @benma, I'd prefer to check in generated sources. We already do it in py/bitbox02 here and in api-go, api-js. Also, similar with vendoring Go pkgs in the wallet app. It's always nice to rely on git and see what's changed.

Copy link
Contributor

@x1ddos x1ddos Jul 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@x1ddos done

hm.. I still see no args parsing in main.rs. @benma am I looking at the old code or wrong place?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm somehow I didn't push properly. Pushed again now.

tools/prost-build/src/main.rs Outdated Show resolved Hide resolved
src/rust/bitbox02-rust/src/hww/api/api.rs Outdated Show resolved Hide resolved
@benma
Copy link
Collaborator Author

benma commented Jul 2, 2020

Did you try with a cargo +nightly vendor -Z avoid-dev-deps?

it's not a dev dependency though.

@x1ddos
Copy link
Contributor

x1ddos commented Jul 2, 2020

Did you try with a cargo +nightly vendor -Z avoid-dev-deps?

it's not a dev dependency though.

Ah right. Didn't pay enough attention.
What if we temporary fork tokio-rs/bytes and move loom to dev dependencies?

@benma
Copy link
Collaborator Author

benma commented Jul 2, 2020

fork

That helped. 3755 files down to 185 👏

Fork here: https://github.com/digitalbitbox/bytes/tree/bitbox02-firmware-20200702

@benma benma force-pushed the rust-prost-2 branch 2 times, most recently from 334f641 to 3b2c083 Compare July 2, 2020 16:32
@x1ddos
Copy link
Contributor

x1ddos commented Jul 2, 2020

Almost forgot: add clippy to pass over tools/prost-build/ or tools/...? I know it's just 2 lines for code but maybe we'll have more with time.

@NickeZ
Copy link
Collaborator

NickeZ commented Jul 2, 2020

To reiterate what I said in a thread. I think it makes sense to compile the tool when you create the docker image instead of every time you compile the sources. So I think the tool should take "OUT_DIR" and "MESSAGES_DIR" as the two input arguments. You don't need especially sophisticated arg parsing:

fn main() {
    let mut args = std::env::args().into_iter();
    let _ = args.next().unwrap(); // executable name
    let out_dir = args.next().unwrap();
    let messages_dir = args.next().unwrap();
    println!("{} {}", out_dir, messages_dir);
}

@x1ddos x1ddos changed the title Rust protobuf api Rust protobuf API Jul 3, 2020
@NickeZ
Copy link
Collaborator

NickeZ commented Jul 6, 2020

I think clap takes a long time to compile (minutes), so I suggest to not use clap in case you don't need all the bells and whistles. You should also pass --release to cargo run to probably increase execution speed some orders of magnitude.

@benma
Copy link
Collaborator Author

benma commented Jul 6, 2020

I think clap takes a long time to compile (minutes),

Hmm I just recompiled and almost didn't notice the compilation, was very quick, definitely nowhere near a minute.

so I suggest to not use clap in case you don't need all the bells and whistles.

But I really much prefer named args over positional args, and the code is much nicer too :O

--release: can add, though execution speed here does not matter much, feels like nanoseconds anyway.

Edit: tried with --release, actually that seems to slow it down some.

@NickeZ
Copy link
Collaborator

NickeZ commented Jul 6, 2020

Edit: tried with --release, actually that seems to slow it down some.

WAT.. haha

edit: or maybe it slowed down the overall thing because it added more time to the compilation of the tool than it removed from the run time?

@benma
Copy link
Collaborator Author

benma commented Jul 6, 2020

Edit: tried with --release, actually that seems to slow it down some.

WAT.. haha

edit: or maybe it slowed down the overall thing because it added more time to the compilation of the tool than it removed from the run time?

Yeah I meant the compile time, in response to

I think clap takes a long time to compile (minutes),

Run time is probably better, but compiling those handful of proto files is fast no matter what.

@x1ddos
Copy link
Contributor

x1ddos commented Jul 6, 2020

I think clap takes a long time to compile (minutes)

@NickeZ didn't you also suggest to add the compiled tool to the docker image? :)

Also, @benma @NickeZ is clap like a top package cool kids use to parse cmd line args in Rust these days? Why is it taking long to compile anyway? Are there alternatives?

@x1ddos
Copy link
Contributor

x1ddos commented Jul 6, 2020

Re: clap. At the end of the day, how often are we going to regenerate proto sources. I think quite rarely if they're checked in.

Copy link
Contributor

@x1ddos x1ddos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! One other very minor thing:

Almost forgot: add clippy to pass over tools/prost-build/ or tools/...? I know it's just 2 lines for code but maybe we'll have more with time.

license = "Apache-2.0"

[dependencies]
clap = "3.0.0-beta.1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not the latest non-beta release 2.33?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why non-beta? beta is working.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stability? otherwise, why call things "beta".
git master/HEAD is also working. why not always pull from latest git all the deps then :)

Copy link
Collaborator Author

@benma benma Jul 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api stability is out anyway, as 2.33 -> 3.0.0 is breaking. I expect less work to upgrade to 3.0.0 from the beta than from 2.33. Probably no work, would be a surprise if they changed the api that breaks our 4 line use of it.

If you insist I can do it, but imho it's working okay ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect less work to upgrade to 3.0.0 from the beta than from 2.33

That's the explanation I expected! Makes sense. Good as is then, considering it's 4 lines like you said. 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, not expected really but it makes sense either way.

@benma
Copy link
Collaborator Author

benma commented Jul 9, 2020

how often

CI will do it everytime, right, as to the CI, the .proto files are always new. So probably worth it to put it into the Docker image eventually to shorten CI time.

@x1ddos
Copy link
Contributor

x1ddos commented Jul 9, 2020

how often

CI will do it everytime, right, as to the CI, the .proto files are always new. So probably worth it to put it into the Docker image eventually to shorten CI time.

Exactly, so that should make long compile time of clap less annoying.

@x1ddos
Copy link
Contributor

x1ddos commented Jul 9, 2020

So, what's left here, this?

Still missing: commander_states_can_call() check from commander() - I didn't add it yet to keep the code smaller

and clippy to run over tools/...

@benma
Copy link
Collaborator Author

benma commented Jul 9, 2020

and clippy to run over tools/...

I am not sure my cmake skills are up to the task. The only thing I can come up is to make another custom target like rust-clippy-tools, which kinda sucks, but it is in a different WORKING_DIRECTORY. i am inclined to just skip it, seeing that the tool is totally trivial.

@x1ddos
Copy link
Contributor

x1ddos commented Jul 10, 2020 via email

@benma
Copy link
Collaborator Author

benma commented Aug 17, 2020

Rebased.

We make a small cli tool, used in CMakeLists, similar to nanopb.

The generated file lands in the source folder and is committed. I
prefer this over generating into the build folder. The advantages are:

- Less reliance on the tool to work when you check out and old commit
- The rust crates do not depend on CMake in this way, which should
make it easier to use standard Rust tooling (though more work is
- needed there).
Used in bitbox02-rust to decode and encode protobuf messages.
@benma
Copy link
Collaborator Author

benma commented Aug 22, 2020

So, what's left here, this?

Still missing: commander_states_can_call() check from commander() - I didn't add it yet to keep the code smaller

Added a commit resolving this part too, PTAL.

Imho we shouldn't wait longer for the prost release, and use the GitHub dependency as is in this PR. The release will come when it will come. In the meantime, we shouldn't be blocked from going further with Rust api call processing. WDYT?

@benma benma force-pushed the rust-prost-2 branch 3 times, most recently from 5907bd5 to 523c79c Compare August 22, 2020 22:03
@x1ddos
Copy link
Contributor

x1ddos commented Aug 24, 2020

Imho we shouldn't wait longer for the prost release, and use the GitHub dependency as is in this PR. The release will come when it will come. In the meantime, we shouldn't be blocked from going further with Rust api call processing. WDYT?

👍 let me try this on a dev device...

Copy link
Contributor

@x1ddos x1ddos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears clippy isn't running and a tiny bit unhappy. We should add it to the CI.

return mock();
}

static void _test_api_set_device_name(void** state)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too bad this commit deletes a unit test without an equivalent replacement, or there one in rust that I don't see?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not, any chance to export commander_api_set_device_name from rust to use here in the test?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imho we should start thinking about how to properly unit test Rust code that references C code, and then add some tests for all code. Propably will involve some extra traits, but not quite clear. This has been a blocker to write good unit tests in Rust in general. Happy to maximize coverage then.

There is no big loss in removing this test until we have that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

/// `input` is a hww.proto Request message, protobuf encoded.
/// Returns a protobuf encoded hww.proto Response message.
pub async fn process(input: Vec<u8>) -> Vec<u8> {
use prost::Message;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we agreed to use global imports with an exception of enums where makes sense, or did I misunderstood?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this line preceeds this discussion.

I plan to split the individual api calls into individual files after this is merged, so I would move the use to the top level then, ok?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Collaborator Author

@benma benma Aug 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I thought we were in the device name api function. Fixed it for prost::Message, which will stay in this file even when I move the api calls to other files.

// All A-Okay.
will_return(__wrap_workflow_confirm_blocking, true);
will_return(__wrap_memory_set_device_name, true);
assert_int_equal(COMMANDER_OK, commander_api_set_device_name(&request));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The func declaration is still in test/unit-test/framework/includes/test_commander.h. That should be removed too, right.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed!

@benma
Copy link
Collaborator Author

benma commented Aug 24, 2020

It appears clippy isn't running and a tiny bit unhappy. We should add it to the CI.

fixed and added to CI. PTAL

benma added 4 commits August 25, 2020 00:06
Moving the commander states check calls from C to Rust makes sure that
API calls handled in Rust obey the same rules, e.g. that SetDeviceName
cannot be called in the middle of a Bitcoin tx signing session.
api::api is bad (clippy), so we move it to mod.rs.
@x1ddos
Copy link
Contributor

x1ddos commented Aug 25, 2020

LGTM. Hope to see more tests soon!

@benma benma merged commit e9802c2 into BitBoxSwiss:master Aug 25, 2020
@benma benma deleted the rust-prost-2 branch August 25, 2020 08:57
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

Successfully merging this pull request may close these issues.

3 participants