Skip to content

Add no_std support #277

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

Merged
merged 1 commit into from
Jun 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Checkout Crate
uses: actions/checkout@v2
- name: Install hongfuzz dependancies
run: sudo apt install build-essential binutils-dev libunwind-dev libblocksruntime-dev liblzma-dev
run: sudo apt update && sudo apt install build-essential binutils-dev libunwind-dev libblocksruntime-dev liblzma-dev
- name: Checkout Toolchain
uses: actions-rs/toolchain@v1
with:
Expand All @@ -24,6 +24,7 @@ jobs:
- name: Running fuzzer
env:
DO_FUZZ: true
DO_NO_STD: true
run: ./contrib/test.sh

Nightly:
Expand All @@ -41,6 +42,7 @@ jobs:
- name: Running benchmarks
env:
DO_BENCH: true
DO_NO_STD: true
run: ./contrib/test.sh
- name: Building docs
env:
Expand All @@ -56,7 +58,13 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, beta, nightly, 1.41.1]
include:
- rust: stable
- rust: beta
- rust: nightly
- rust: 1.41.1
- rust: 1.47
DO_NO_STD: true
steps:
- name: Checkout Crate
uses: actions/checkout@v2
Expand All @@ -69,4 +77,26 @@ jobs:
- name: Running cargo
env:
DO_FEATURE_MATRIX: true
run: ./contrib/test.sh
DO_NO_STD: ${{ matrix.DO_NO_STD }}
run: ./contrib/test.sh

Embedded:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
run: sudo apt update && sudo apt install -y 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 -nographic -semihosting-config enable=on,target=native -kernel"
run: cd embedded && cargo run --target thumbv7m-none-eabi --release
16 changes: 12 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,44 @@ readme = "README.md"
edition = "2018"

[features]
default = ["std"]
std = ["bitcoin/std", "bitcoin/secp-recovery"]
no-std = ["hashbrown", "bitcoin/no-std"]
compiler = []
trace = []
unstable = []
default = []
use-serde = ["serde", "bitcoin/use-serde"]
rand = ["bitcoin/rand"]

[dependencies]
bitcoin = "0.28.0"
serde = { version = "1.0", optional = true}
bitcoin = { version = "0.28.1", default-features = false }
serde = { version = "1.0", optional = true }
hashbrown = { version = "0.11", optional = true }

[dev-dependencies]
bitcoind = {version = "0.26.1", features=["22_0"]}
actual-rand = { package = "rand", version = "0.8.4"}

[[example]]
name = "htlc"
required-features = ["compiler"]
required-features = ["std", "compiler"]

[[example]]
name = "parse"
required-features = ["std"]

[[example]]
name = "sign_multisig"
required-features = ["std"]

[[example]]
name = "verify_tx"
required-features = ["std"]

[[example]]
name = "psbt"
required-features = ["std"]

[[example]]
name = "xpub_descriptors"
required-features = ["std"]
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ are convertible to `bitcoin::PublicKey`
completing an unsigned `bitcoin::TxIn` with appropriate data
* Determining the specific keys, hash preimages and timelocks used to spend
coins in a given Bitcoin transaction
* `no_std` support enabled by disabling the `default-features` and enabling
`"no-std"`. See `embedded/` for an example.

More information can be found in [the documentation](https://docs.rs/miniscript)
or in [the `examples/` directory](https://github.com/apoelstra/rust-miniscript/tree/master/examples)


## Minimum Supported Rust Version (MSRV)
This library should always compile with any combination of features on **Rust 1.41.1**.
This library should always compile with any combination of features (minus
`no-std`) on **Rust 1.41.1** or **Rust 1.47** with `no-std`.

## Contributing
Contributions are generally welcome. If you intend to make larger changes please
Expand Down
18 changes: 18 additions & 0 deletions contrib/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ then
cargo run --example xpub_descriptors
fi

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

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

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

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

# Bench if told to (this only works with the nightly toolchain)
if [ "$DO_BENCH" = true ]
then
Expand Down
28 changes: 28 additions & 0 deletions embedded/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
authors = [
"Riccardo Casatta <[email protected]>",
"Dev Random <[email protected]>",
]
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"
miniscript = { path = "../", default-features = false, features = ["no-std"] }

[[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
opt-level = "z"
22 changes: 22 additions & 0 deletions embedded/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 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 +nightly --target thumbv7m-none-eabi
```

Output should be something like:

```text
heap size 1048576
descriptor sh(wsh(or_d(c:pk_k(020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261),c:pk_k(0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352))))
p2sh address 3CJxbQBfWAe1ZkKiGQNEYrioV73ZwvBWns
```
5 changes: 5 additions & 0 deletions embedded/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 2048K
RAM : ORIGIN = 0x20000000, LENGTH = 512K
}
2 changes: 2 additions & 0 deletions embedded/scripts/env.sh
Original file line number Diff line number Diff line change
@@ -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 -nographic -semihosting-config enable=on,target=native -kernel"
3 changes: 3 additions & 0 deletions embedded/scripts/install-deps
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

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

extern crate alloc;

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

use alloc_cortex_m::CortexMHeap;

use core::str::FromStr;

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 * 256; // 256 KB

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

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

// begin miniscript test
let descriptor = "sh(wsh(or_d(\
c:pk_k(020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261),\
c:pk_k(0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352)\
)))";
hprintln!("descriptor {}", descriptor).unwrap();
let desc =
miniscript::Descriptor::<miniscript::bitcoin::PublicKey>::from_str(descriptor).unwrap();

// Derive the P2SH address
let p2sh_addr = desc
.address(miniscript::bitcoin::Network::Bitcoin)
.unwrap()
.to_string();
hprintln!("p2sh address {}", p2sh_addr).unwrap();
assert_eq!(p2sh_addr, "3CJxbQBfWAe1ZkKiGQNEYrioV73ZwvBWns");

// Check whether the descriptor is safe
// This checks whether all spend paths are accessible in bitcoin network.
// It maybe possible that some of the spend require more than 100 elements in Wsh scripts
// Or they contain a combination of timelock and heightlock.
assert!(desc.sanity_check().is_ok());

// Estimate the satisfaction cost
assert_eq!(desc.max_satisfaction_weight().unwrap(), 293);
// end miniscript test

// 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: 3 additions & 2 deletions src/descriptor/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
//! Also includes pk, and pkh descriptors
//!

use std::fmt;
use std::str::FromStr;
use core::fmt;
use core::str::FromStr;

use bitcoin::blockdata::script;
use bitcoin::{Address, Network, Script};
Expand All @@ -28,6 +28,7 @@ use super::checksum::{desc_checksum, verify_checksum};
use crate::expression::{self, FromTree};
use crate::miniscript::context::ScriptContext;
use crate::policy::{semantic, Liftable};
use crate::prelude::*;
use crate::util::{varint_len, witness_to_scriptsig};
use crate::{
BareCtx, Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey,
Expand Down
5 changes: 3 additions & 2 deletions src/descriptor/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
//! This module contains a re-implementation of the function used by Bitcoin Core to calculate the
//! checksum of a descriptor

use std::iter::FromIterator;
use core::iter::FromIterator;

use crate::prelude::*;
use crate::Error;

const INPUT_CHARSET: &str = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
Expand Down Expand Up @@ -100,7 +101,7 @@ pub(super) fn verify_checksum(s: &str) -> Result<&str, Error> {
}
#[cfg(test)]
mod test {
use std::str;
use core::str;

use super::*;

Expand Down
Loading