Skip to content

Commit

Permalink
Merge pull request #603 from devrandom/no_std_devrandom
Browse files Browse the repository at this point in the history
no_std support, keeping MSRV
  • Loading branch information
apoelstra committed Jul 20, 2021
2 parents abff973 + bba57d7 commit 5e6b56a
Show file tree
Hide file tree
Showing 48 changed files with 588 additions and 185 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/rust.yml
Expand Up @@ -13,14 +13,17 @@ jobs:
env:
DO_COV: true
AS_DEPENDENCY: true
DO_NO_STD: true
- rust: beta
env:
AS_DEPENDENCY: true
DO_NO_STD: true
- rust: nightly
env:
DO_FUZZ: true
DO_BENCH: true
AS_DEPENDENCY: true
DO_NO_STD: true
- rust: 1.29.0
env:
AS_DEPENDENCY: true
Expand Down Expand Up @@ -54,3 +57,24 @@ jobs:
override: true
- name: Create Doc
run: cargo doc
Embedded:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
run: sudo apt update && sudo apt install qemu-system-arm gcc-arm-none-eabi
- name: Checkout Toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rust-src
target: thumbv7m-none-eabi
- name: Run
env:
RUSTFLAGS: "-C link-arg=-Tlink.x"
CARGO_TARGET_THUMBV7M_NONE_EABI_RUNNER: "qemu-system-arm -cpu cortex-m3 -machine mps2-an385 -m 1G -nographic -semihosting-config enable=on,target=native -kernel"
run: cd embedded && cargo run --target thumbv7m-none-eabi

25 changes: 21 additions & 4 deletions Cargo.toml
Expand Up @@ -12,7 +12,7 @@ readme = "README.md"


[features]
default = [ "secp-recovery" ]
default = [ "std", "secp-recovery" ]
base64 = [ "base64-compat" ]
fuzztarget = []
unstable = []
Expand All @@ -21,14 +21,24 @@ use-serde = ["serde", "bitcoin_hashes/serde", "secp256k1/serde"]
secp-lowmemory = ["secp256k1/lowmemory"]
secp-recovery = ["secp256k1/recovery"]

# At least one of std, no-std must be enabled.
#
# The no-std feature doesn't disable std - you need to turn off the std feature for that by disabling default.
# Instead no-std enables additional features required for this crate to be usable without std.
# As a result, both can be enabled without conflict.
std = ["secp256k1/std", "bitcoin_hashes/std", "bech32/std"]
no-std = ["hashbrown", "core2/alloc", "bitcoin_hashes/alloc"]

[dependencies]
bech32 = "0.8.0"
bitcoin_hashes = "0.9.6"
secp256k1 = "0.20.2"
bech32 = { version = "0.8.1", default-features = false }
bitcoin_hashes = { version = "0.10.0", default-features = false }
secp256k1 = { version = "0.20.2", default-features = false }
core2 = { version = "0.3.0", optional = true, default-features = false }

base64-compat = { version = "1.0.0", optional = true }
bitcoinconsensus = { version = "0.19.0-3", optional = true }
serde = { version = "1", features = [ "derive" ], optional = true }
hashbrown = { version = "0.8", optional = true }

[dev-dependencies]
serde_json = "<1.0.45"
Expand All @@ -37,3 +47,10 @@ secp256k1 = { version = "0.20.0", features = [ "recovery", "rand-std" ] }
bincode = "1.3.1"
# We need to pin ryu (transitive dep from serde_json) to stay compatible with Rust 1.22.0
ryu = "<1.0.5"

[[example]]
name = "bip32"

[[example]]
name = "handshake"
required-features = ["std"]
49 changes: 39 additions & 10 deletions contrib/test.sh
@@ -1,6 +1,12 @@
#!/bin/sh -ex

FEATURES="base64 bitcoinconsensus use-serde rand"
FEATURES="base64 bitcoinconsensus use-serde rand secp-recovery"

# Use toolchain if explicitly specified
if [ -n "$TOOLCHAIN" ]
then
alias cargo="cargo +$TOOLCHAIN"
fi

pin_common_verions() {
cargo generate-lockfile --verbose
Expand All @@ -10,7 +16,7 @@ pin_common_verions() {
}

# Pin `cc` for Rust 1.29
if [ -n "$PIN_VERSIONS" ]; then
if [ "$PIN_VERSIONS" = true ]; then
pin_common_verions
cargo update -p byteorder --precise "1.3.4"
fi
Expand All @@ -21,20 +27,43 @@ then
fi


# Use toolchain if explicitly specified
if [ -n "$TOOLCHAIN" ]
then
alias cargo="cargo +$TOOLCHAIN"
fi
echo "********* Testing std *************"
# Test without any features other than std first
cargo test --verbose --no-default-features --features="std"

# Test without any features first
cargo test --verbose --no-default-features
echo "********* Testing default *************"
# Then test with the default features
cargo test --verbose

if [ "$DO_NO_STD" = true ]
then
echo "********* Testing no-std build *************"
# Build no_std, to make sure that cfg(test) doesn't hide any issues
cargo build --verbose --features="no-std" --no-default-features

# Build std + no_std, to make sure they are not incompatible
cargo build --verbose --features="no-std"

# Test no_std
cargo test --verbose --features="no-std" --no-default-features

# Build all features
cargo build --verbose --features="no-std $FEATURES" --no-default-features

# Build specific features
for feature in ${FEATURES}
do
cargo build --verbose --features="no-std $feature"
done

cargo run --example bip32 L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D
cargo run --no-default-features --features no-std --example bip32 L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D
fi

# Test each feature
for feature in ${FEATURES}
do
echo "********* Testing "$feature" *************"
cargo test --verbose --features="$feature"
done

Expand All @@ -55,7 +84,7 @@ then
fi

# Use as dependency if told to
if [ -n "$AS_DEPENDENCY" ]
if [ "$AS_DEPENDENCY" = true ]
then
cargo new dep_test
cd dep_test
Expand Down
24 changes: 24 additions & 0 deletions embedded/Cargo.toml
@@ -0,0 +1,24 @@
[package]
authors = ["Riccardo Casatta <riccardo@casatta.it>", "Dev Random <c1.devrandom@niftybox.net>"]
edition = "2018"
readme = "README.md"
name = "embedded"
version = "0.1.0"

[dependencies]
cortex-m = "0.6.0"
cortex-m-rt = "0.6.10"
cortex-m-semihosting = "0.3.3"
panic-halt = "0.2.0"
alloc-cortex-m = "0.4.1"
bitcoin = { path="../", default-features = false, features = ["no-std", "secp-lowmemory"] }

[[bin]]
name = "embedded"
test = false
bench = false

[profile.release]
codegen-units = 1 # better optimizations
debug = true # symbols are nice and they don't increase the size on Flash
lto = true # better optimizations
25 changes: 25 additions & 0 deletions embedded/README.md
@@ -0,0 +1,25 @@
# Running

To run the embedded test, first prepare your environment:

```shell
sudo ./scripts/install-deps
rustup target add thumbv7m-none-eabi
```

Then:

```shell
source ./scripts/env.sh && cargo run --target thumbv7m-none-eabi
```

Output should be something like:

```text
heap size 524288
secp buf size 66240
Seed WIF: L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D
Address: bc1qpx9t9pzzl4qsydmhyt6ctrxxjd4ep549np9993
```

Note that this heap size is required because of the amount of stack used by libsecp256k1 when initializing a context.
5 changes: 5 additions & 0 deletions embedded/memory.x
@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 512K
}
2 changes: 2 additions & 0 deletions embedded/scripts/env.sh
@@ -0,0 +1,2 @@
export RUSTFLAGS="-C link-arg=-Tlink.x"
export CARGO_TARGET_THUMBV7M_NONE_EABI_RUNNER="qemu-system-arm -cpu cortex-m3 -machine mps2-an385 -m 1G -nographic -semihosting-config enable=on,target=native -kernel"
3 changes: 3 additions & 0 deletions embedded/scripts/install-deps
@@ -0,0 +1,3 @@
#!/bin/sh

apt install gcc-arm-none-eabi qemu-system-arm gdb-multiarch
76 changes: 76 additions & 0 deletions embedded/src/main.rs
@@ -0,0 +1,76 @@
#![feature(alloc_error_handler)]
#![feature(panic_info_message)]
#![no_std]
#![no_main]

extern crate alloc;
extern crate bitcoin;

use alloc::string::ToString;
use alloc::vec;
use core::alloc::Layout;
use core::panic::PanicInfo;

use alloc_cortex_m::CortexMHeap;
// use panic_halt as _;
use bitcoin::{Address, Network, PrivateKey};
use bitcoin::secp256k1::ffi::types::AlignedType;
use bitcoin::secp256k1::Secp256k1;

use cortex_m::asm;
use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln};

// this is the allocator the application will use
#[global_allocator]
static ALLOCATOR: CortexMHeap = CortexMHeap::empty();

const HEAP_SIZE: usize = 1024 * 512; // 512 KB

#[entry]
fn main() -> ! {
hprintln!("heap size {}", HEAP_SIZE).unwrap();

unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) }

let size = Secp256k1::preallocate_size();
hprintln!("secp buf size {}", size*16).unwrap();

// Load a private key
let raw = "L1HKVVLHXiUhecWnwFYF6L3shkf1E12HUmuZTESvBXUdx3yqVP1D";
let pk = PrivateKey::from_wif(raw).unwrap();
hprintln!("Seed WIF: {}", pk).unwrap();

let mut buf_ful = vec![AlignedType::zeroed(); size];
let secp = Secp256k1::preallocated_new(&mut buf_ful).unwrap();

// Derive address
let pubkey = pk.public_key(&secp);
let address = Address::p2wpkh(&pubkey, Network::Bitcoin).unwrap();
hprintln!("Address: {}", address).unwrap();

assert_eq!(address.to_string(), "bc1qpx9t9pzzl4qsydmhyt6ctrxxjd4ep549np9993".to_string());
// exit QEMU
// NOTE do not run this on hardware; it can corrupt OpenOCD state
debug::exit(debug::EXIT_SUCCESS);

loop {}
}

// define what happens in an Out Of Memory (OOM) condition
#[alloc_error_handler]
fn alloc_error(_layout: Layout) -> ! {
hprintln!("alloc error").unwrap();
debug::exit(debug::EXIT_FAILURE);
asm::bkpt();

loop {}
}

#[inline(never)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
hprintln!("panic {:?}", info.message()).unwrap();
debug::exit(debug::EXIT_FAILURE);
loop {}
}
5 changes: 4 additions & 1 deletion examples/bip32.rs
Expand Up @@ -10,6 +10,7 @@ use bitcoin::util::bip32::ExtendedPubKey;
use bitcoin::util::bip32::DerivationPath;
use bitcoin::util::bip32::ChildNumber;
use bitcoin::util::address::Address;
use bitcoin::secp256k1::ffi::types::AlignedType;

fn main() {
// This example derives root xprv
Expand All @@ -36,7 +37,9 @@ fn main() {
let seed = wif.to_bytes();

// we need secp256k1 context for key derivation
let secp = Secp256k1::new();
let mut buf: Vec<AlignedType> = Vec::new();
buf.resize(Secp256k1::preallocate_size(), AlignedType::zeroed());
let secp = Secp256k1::preallocated_new(buf.as_mut_slice()).unwrap();

// calculate root key from seed
let root = ExtendedPrivKey::new_master(network, &seed).unwrap();
Expand Down
3 changes: 3 additions & 0 deletions src/blockdata/block.rs
Expand Up @@ -20,6 +20,8 @@
//! these blocks and the blockchain.
//!

use prelude::*;

use core::fmt;

use util;
Expand Down Expand Up @@ -308,6 +310,7 @@ impl fmt::Display for Bip34Error {
}
}

#[cfg(feature = "std")]
impl ::std::error::Error for Bip34Error {}

#[cfg(test)]
Expand Down
9 changes: 7 additions & 2 deletions src/blockdata/constants.rs
Expand Up @@ -19,9 +19,11 @@
//! single transaction
//!

use prelude::*;

use core::default::Default;

use hashes::hex::FromHex;
use hashes::hex::{HexIterator, Error as HexError};
use hashes::sha256d;
use blockdata::opcodes;
use blockdata::script;
Expand Down Expand Up @@ -84,8 +86,11 @@ fn bitcoin_genesis_tx() -> Transaction {
});

// Outputs
let script_bytes: Result<Vec<u8>, HexError> =
HexIterator::new("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap()
.collect();
let out_script = script::Builder::new()
.push_slice(&Vec::from_hex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap())
.push_slice(script_bytes.unwrap().as_slice())
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script();
ret.output.push(TxOut {
Expand Down
2 changes: 2 additions & 0 deletions src/blockdata/opcodes.rs
Expand Up @@ -22,6 +22,8 @@

#[cfg(feature = "serde")] use serde;

#[cfg(feature = "serde")] use prelude::*;

use core::{fmt, convert::From};

// Note: I am deliberately not implementing PartialOrd or Ord on the
Expand Down

0 comments on commit 5e6b56a

Please sign in to comment.