From 4826d0c6cc69c114c005cde1b21e832de935d731 Mon Sep 17 00:00:00 2001 From: Devrandom Date: Wed, 9 Jun 2021 12:34:44 +0200 Subject: [PATCH 1/2] no_std support Based on the original work by Justin Moon. *MSRV unchanged from 1.29.0.* When `std` is off, `no-std` must be on, and we use the [`alloc`](https://doc.rust-lang.org/alloc/) and core2 crates. The `alloc` crate requires the user define a global allocator. * Import from `core` and `alloc` instead of `std` * `alloc` only used if `no-std` is on * Create `std` feature * Create `no-std` feature which adds a core2 dependency to polyfill `std::io` features. This is an experimental feature and should be used with caution. * CI runs tests `no-std` * MSRV for `no-std` is 1.51 or so --- .github/workflows/rust.yml | 3 ++ Cargo.toml | 25 +++++++++-- contrib/test.sh | 49 +++++++++++++++----- examples/bip32.rs | 5 ++- src/blockdata/block.rs | 3 ++ src/blockdata/constants.rs | 9 +++- src/blockdata/opcodes.rs | 2 + src/blockdata/script.rs | 12 ++--- src/blockdata/transaction.rs | 11 +++-- src/consensus/encode.rs | 47 +++++++++++--------- src/internal_macros.rs | 16 ++++--- src/lib.rs | 76 ++++++++++++++++++++++++++++++-- src/network/address.rs | 1 + src/network/message.rs | 3 +- src/network/message_blockdata.rs | 4 +- src/network/message_network.rs | 3 +- src/network/mod.rs | 18 ++++---- src/network/stream_reader.rs | 2 + src/serde_utils.rs | 44 +++++++++--------- src/util/address.rs | 13 +++--- src/util/amount.rs | 16 +++++-- src/util/base58.rs | 6 ++- src/util/bip143.rs | 2 + src/util/bip158.rs | 24 +++++----- src/util/bip32.rs | 7 ++- src/util/contracthash.rs | 5 ++- src/util/ecdsa.rs | 24 +++++++--- src/util/hash.rs | 2 + src/util/key.rs | 5 ++- src/util/merkleblock.rs | 54 ++++++++++++++++------- src/util/misc.rs | 6 ++- src/util/mod.rs | 24 ++++++++-- src/util/psbt/error.rs | 6 ++- src/util/psbt/macros.rs | 10 ++--- src/util/psbt/map/global.rs | 26 +++++------ src/util/psbt/map/input.rs | 15 ++++--- src/util/psbt/map/mod.rs | 2 + src/util/psbt/map/output.rs | 12 ++--- src/util/psbt/mod.rs | 11 +++-- src/util/psbt/raw.rs | 7 +-- src/util/psbt/serialize.rs | 2 + src/util/uint.rs | 5 ++- 42 files changed, 432 insertions(+), 185 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c2ac4fa651..0ab90f3114 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index bc7e64a8c4..cb8dfa0ba0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" [features] -default = [ "secp-recovery" ] +default = [ "std", "secp-recovery" ] base64 = [ "base64-compat" ] fuzztarget = [] unstable = [] @@ -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" @@ -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"] diff --git a/contrib/test.sh b/contrib/test.sh index 55f43f6b93..5f61ccc7bb 100755 --- a/contrib/test.sh +++ b/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 @@ -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 @@ -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 @@ -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 diff --git a/examples/bip32.rs b/examples/bip32.rs index 3c73501db2..0b0d1f27ef 100644 --- a/examples/bip32.rs +++ b/examples/bip32.rs @@ -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 @@ -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 = 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(); diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs index ce10fdb1fb..0c53ed42ec 100644 --- a/src/blockdata/block.rs +++ b/src/blockdata/block.rs @@ -20,6 +20,8 @@ //! these blocks and the blockchain. //! +use prelude::*; + use core::fmt; use util; @@ -308,6 +310,7 @@ impl fmt::Display for Bip34Error { } } +#[cfg(feature = "std")] impl ::std::error::Error for Bip34Error {} #[cfg(test)] diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs index e8c54946a5..af639f41bd 100644 --- a/src/blockdata/constants.rs +++ b/src/blockdata/constants.rs @@ -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; @@ -84,8 +86,11 @@ fn bitcoin_genesis_tx() -> Transaction { }); // Outputs + let script_bytes: Result, 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 { diff --git a/src/blockdata/opcodes.rs b/src/blockdata/opcodes.rs index b6d716ac1a..bf1be28a85 100644 --- a/src/blockdata/opcodes.rs +++ b/src/blockdata/opcodes.rs @@ -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 diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index 3826bb1ca1..42ecab584c 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -24,8 +24,9 @@ //! This module provides the structures and functions needed to support scripts. //! -use io; +use prelude::*; +use io; use core::{fmt, default::Default}; #[cfg(feature = "serde")] use serde; @@ -36,7 +37,7 @@ use consensus::{encode, Decodable, Encodable}; use hashes::{Hash, hex}; use policy::DUST_RELAY_TX_FEE; #[cfg(feature="bitcoinconsensus")] use bitcoinconsensus; -#[cfg(feature="bitcoinconsensus")] use std::convert; +#[cfg(feature="bitcoinconsensus")] use core::convert::From; #[cfg(feature="bitcoinconsensus")] use OutPoint; use util::ecdsa::PublicKey; @@ -145,11 +146,12 @@ impl fmt::Display for Error { } } +#[cfg(feature = "std")] impl ::std::error::Error for Error {} #[cfg(feature="bitcoinconsensus")] #[doc(hidden)] -impl convert::From for Error { +impl From for Error { fn from(err: bitcoinconsensus::Error) -> Error { match err { _ => Error::BitcoinConsensus(err) @@ -421,11 +423,11 @@ impl Script { } else if self.is_witness_program() { 32 + 4 + 1 + (107 / 4) + 4 + // The spend cost copied from Core 8 + // The serialized size of the TxOut's amount field - self.consensus_encode(&mut ::std::io::sink()).unwrap() as u64 // The serialized size of this script_pubkey + self.consensus_encode(&mut sink()).unwrap() as u64 // The serialized size of this script_pubkey } else { 32 + 4 + 1 + 107 + 4 + // The spend cost copied from Core 8 + // The serialized size of the TxOut's amount field - self.consensus_encode(&mut ::std::io::sink()).unwrap() as u64 // The serialized size of this script_pubkey + self.consensus_encode(&mut sink()).unwrap() as u64 // The serialized size of this script_pubkey }; ::Amount::from_sat(sats) diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index f9bc4a2131..4ecac18a21 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -23,9 +23,11 @@ //! This module provides the structures and functions needed to support transactions. //! +use prelude::*; + use io; use core::{fmt, str, default::Default}; -use std::error; +#[cfg(feature = "std")] use std::error; use hashes::{self, Hash, sha256d}; use hashes::hex::FromHex; @@ -130,8 +132,9 @@ impl fmt::Display for ParseOutPointError { } } -impl error::Error for ParseOutPointError { - fn cause(&self) -> Option<&dyn error::Error> { +#[cfg(feature = "std")] +impl error::Error for ParseOutPointError { + fn cause(&self) -> Option<&dyn error::Error> { match *self { ParseOutPointError::Txid(ref e) => Some(e), ParseOutPointError::Vout(ref e) => Some(e), @@ -630,6 +633,7 @@ impl fmt::Display for NonStandardSigHashType { } } +#[cfg(feature = "std")] impl error::Error for NonStandardSigHashType {} /// Hashtype of an input's signature, encoded in the last byte of the signature @@ -1360,6 +1364,7 @@ mod tests { use hashes::hex::FromHex; use std::collections::HashMap; use blockdata::script; + // a random recent segwit transaction from blockchain using both old and segwit inputs let mut spending: Transaction = deserialize(Vec::from_hex("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700") .unwrap().as_slice()).unwrap(); diff --git a/src/consensus/encode.rs b/src/consensus/encode.rs index f538f5b51a..7fae244af5 100644 --- a/src/consensus/encode.rs +++ b/src/consensus/encode.rs @@ -29,22 +29,23 @@ //! big-endian decimals, etc.) //! +use prelude::*; + use core::{fmt, mem, u32, convert::From}; -use std::borrow::Cow; -use std::error; -use hashes::hex::ToHex; +#[cfg(feature = "std")] use std::error; use hashes::{sha256d, Hash}; use hash_types::{BlockHash, FilterHash, TxMerkleNode, FilterHeader}; -use io::{self, Cursor, Read, Write}; +use io::{self, Cursor, Read}; use util::endian; use util::psbt; +use hashes::hex::ToHex; use blockdata::transaction::{TxOut, Transaction, TxIn}; -use network::message_blockdata::Inventory; -use network::address::{Address, AddrV2Message}; +#[cfg(feature = "std")] +use network::{message_blockdata::Inventory, address::{Address, AddrV2Message}}; /// Encoding error #[derive(Debug)] @@ -104,8 +105,9 @@ impl fmt::Display for Error { } } -impl error::Error for Error { - fn cause(&self) -> Option<&dyn error::Error> { +#[cfg(feature = "std")] +impl ::std::error::Error for Error { + fn cause(&self) -> Option<&dyn error::Error> { match *self { Error::Io(ref e) => Some(e), Error::Psbt(ref e) => Some(e), @@ -240,7 +242,7 @@ macro_rules! decoder_fn { ($name:ident, $val_type:ty, $readfn:ident, $byte_len: expr) => { #[inline] fn $name(&mut self) -> Result<$val_type, Error> { - debug_assert_eq!(::std::mem::size_of::<$val_type>(), $byte_len); // size_of isn't a constfn in 1.22 + debug_assert_eq!(::core::mem::size_of::<$val_type>(), $byte_len); // size_of isn't a constfn in 1.22 let mut val = [0; $byte_len]; self.read_exact(&mut val[..]).map_err(Error::Io)?; Ok(endian::$readfn(&val)) @@ -248,7 +250,7 @@ macro_rules! decoder_fn { } } -impl WriteExt for W { +impl WriteExt for W { encoder_fn!(emit_u64, u64, u64_to_array_le); encoder_fn!(emit_u32, u32, u32_to_array_le); encoder_fn!(emit_u16, u16, u16_to_array_le); @@ -591,11 +593,12 @@ impl_vec!(TxMerkleNode); impl_vec!(Transaction); impl_vec!(TxOut); impl_vec!(TxIn); -impl_vec!(Inventory); impl_vec!(Vec); -impl_vec!((u32, Address)); impl_vec!(u64); -impl_vec!(AddrV2Message); + +#[cfg(feature = "std")] impl_vec!(Inventory); +#[cfg(feature = "std")] impl_vec!((u32, Address)); +#[cfg(feature = "std")] impl_vec!(AddrV2Message); fn consensus_encode_with_size(data: &[u8], mut s: S) -> Result { let vi_len = VarInt(data.len() as u64).consensus_encode(&mut s)?; @@ -694,13 +697,13 @@ impl<'a, T: Encodable> Encodable for &'a mut T { } } -impl Encodable for ::std::rc::Rc { +impl Encodable for rc::Rc { fn consensus_encode(&self, s: S) -> Result { (&**self).consensus_encode(s) } } -impl Encodable for ::std::sync::Arc { +impl Encodable for sync::Arc { fn consensus_encode(&self, s: S) -> Result { (&**self).consensus_encode(s) } @@ -763,8 +766,8 @@ mod tests { use consensus::{Encodable, deserialize_partial, Decodable}; use util::endian::{u64_to_array_le, u32_to_array_le, u16_to_array_le}; use secp256k1::rand::{thread_rng, Rng}; - use network::message_blockdata::Inventory; - use network::Address; + #[cfg(feature = "std")] + use network::{Address, message_blockdata::Inventory}; #[test] fn serialize_int_test() { @@ -839,7 +842,7 @@ mod tests { } fn test_varint_len(varint: VarInt, expected: usize) { - let mut encoder = io::Cursor::new(vec![]); + let mut encoder = vec![]; assert_eq!(varint.consensus_encode(&mut encoder).unwrap(), expected); assert_eq!(varint.len(), expected); } @@ -970,10 +973,12 @@ mod tests { test_len_is_max_vec::(); test_len_is_max_vec::(); test_len_is_max_vec::(); - test_len_is_max_vec::(); test_len_is_max_vec::>(); - test_len_is_max_vec::<(u32, Address)>(); test_len_is_max_vec::(); + #[cfg(feature = "std")] + test_len_is_max_vec::<(u32, Address)>(); + #[cfg(feature = "std")] + test_len_is_max_vec::(); } fn test_len_is_max_vec() where Vec: Decodable, T: fmt::Debug { @@ -988,7 +993,7 @@ mod tests { assert_eq!(deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(), Some("Andrew".to_string())); assert_eq!( deserialize(&[6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]).ok(), - Some(::std::borrow::Cow::Borrowed("Andrew")) + Some(Cow::Borrowed("Andrew")) ); } diff --git a/src/internal_macros.rs b/src/internal_macros.rs index 8ce74f77d5..3aaf500859 100644 --- a/src/internal_macros.rs +++ b/src/internal_macros.rs @@ -162,7 +162,7 @@ macro_rules! display_from_debug { macro_rules! hex_script (($s:expr) => (<$crate::Script as ::core::str::FromStr>::from_str($s).unwrap())); #[cfg(test)] -macro_rules! hex_hash (($h:ident, $s:expr) => ($h::from_slice(& as $crate::hashes::hex::FromHex>::from_hex($s).unwrap()).unwrap())); +macro_rules! hex_hash (($h:ident, $s:expr) => ($h::from_slice(&<$crate::prelude::Vec as $crate::hashes::hex::FromHex>::from_hex($s).unwrap()).unwrap())); macro_rules! serde_string_impl { ($name:ident, $expecting:expr) => { @@ -563,10 +563,14 @@ macro_rules! user_enum { fn from_str(s: &str) -> Result { match s { $($txt => Ok($name::$elem)),*, - _ => Err($crate::io::Error::new( - $crate::io::ErrorKind::InvalidInput, - format!("Unknown network (type {})", s), - )), + _ => { + #[cfg(not(feature = "std"))] let message = "Unknown network"; + #[cfg(feature = "std")] let message = format!("Unknown network (type {})", s); + Err($crate::io::Error::new( + $crate::io::ErrorKind::InvalidInput, + message, + )) + } } } } @@ -607,7 +611,7 @@ macro_rules! user_enum { self.visit_str(v) } - fn visit_string(self, v: String) -> Result + fn visit_string(self, v: $crate::prelude::String) -> Result where E: $crate::serde::de::Error, { diff --git a/src/lib.rs b/src/lib.rs index 83945b488a..5d04b32ad7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,8 @@ //! software. //! +#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] + // Experimental features we need #![cfg_attr(all(test, feature = "unstable"), feature(test))] @@ -38,12 +40,26 @@ #![deny(unused_must_use)] #![deny(broken_intra_doc_links)] -extern crate core; +#[cfg(not(any(feature = "std", feature = "no-std")))] +compile_error!("at least one of the `std` or `no-std` features must be enabled"); + +#[cfg(feature = "no-std")] +#[macro_use] +extern crate alloc; +#[cfg(feature = "no-std")] +extern crate core2; + +#[cfg(any(feature = "std", test))] +extern crate core; // for Rust 1.29 and no-std tests // Re-exported dependencies. #[macro_use] pub extern crate bitcoin_hashes as hashes; pub extern crate secp256k1; pub extern crate bech32; + +#[cfg(feature = "no-std")] +extern crate hashbrown; + #[cfg(feature = "base64")] pub extern crate base64; #[cfg(feature="bitcoinconsensus")] extern crate bitcoinconsensus; @@ -98,14 +114,68 @@ pub use util::ecdsa::PrivateKey; #[deprecated(since = "0.26.1", note = "Please use `ecdsa::PublicKey` instead")] pub use util::ecdsa::PublicKey; +#[cfg(feature = "std")] use std::io; +#[cfg(not(feature = "std"))] +use core2::io; + +#[cfg(not(feature = "std"))] +mod io_extras { + /// A writer which will move data into the void. + pub struct Sink { + _priv: (), + } + + /// Creates an instance of a writer which will successfully consume all data. + pub const fn sink() -> Sink { + Sink { _priv: () } + } + + impl core2::io::Write for Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> core2::io::Result { + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> core2::io::Result<()> { + Ok(()) + } + } +} + +mod prelude { + #[cfg(all(not(feature = "std"), not(test)))] + pub use alloc::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Cow, ToOwned}, slice, rc, sync}; + + #[cfg(any(feature = "std", test))] + pub use std::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Cow, ToOwned}, slice, rc, sync}; + + #[cfg(all(not(feature = "std"), not(test)))] + pub use alloc::collections::{BTreeMap, btree_map}; + + #[cfg(any(feature = "std", test))] + pub use std::collections::{BTreeMap, btree_map}; + + #[cfg(feature = "std")] + pub use std::io::sink; + + #[cfg(not(feature = "std"))] + pub use io_extras::sink; + + #[cfg(feature = "hashbrown")] + pub use hashbrown::HashSet; + + #[cfg(not(feature = "hashbrown"))] + pub use std::collections::HashSet; +} #[cfg(all(test, feature = "unstable"))] use tests::EmptyWrite; #[cfg(all(test, feature = "unstable"))] mod tests { - use std::io::{IoSlice, Result, Write}; - use std::fmt::Arguments; + use core::fmt::Arguments; + use io::{IoSlice, Result, Write}; #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct EmptyWrite; diff --git a/src/network/address.rs b/src/network/address.rs index 6e88ad2ec5..21874a506f 100644 --- a/src/network/address.rs +++ b/src/network/address.rs @@ -17,6 +17,7 @@ //! This module defines the structures and functions needed to encode //! network addresses in Bitcoin messages. //! +use prelude::*; use core::{fmt, iter}; use std::net::{SocketAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6, Ipv4Addr, ToSocketAddrs}; diff --git a/src/network/message.rs b/src/network/message.rs index 156267ba50..51cb7b0b14 100644 --- a/src/network/message.rs +++ b/src/network/message.rs @@ -19,8 +19,9 @@ //! also defines (de)serialization routines for many primitives. //! +use prelude::*; + use core::{mem, fmt, iter}; -use std::borrow::Cow; use io; use blockdata::block; diff --git a/src/network/message_blockdata.rs b/src/network/message_blockdata.rs index d66a2143c3..7bf7a67582 100644 --- a/src/network/message_blockdata.rs +++ b/src/network/message_blockdata.rs @@ -18,6 +18,8 @@ //! Bitcoin data (blocks and transactions) around. //! +use prelude::*; + use io; use hashes::sha256d; @@ -149,7 +151,7 @@ impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash); #[cfg(test)] mod tests { - use super::{GetHeadersMessage, GetBlocksMessage}; + use super::{Vec, GetHeadersMessage, GetBlocksMessage}; use hashes::hex::FromHex; diff --git a/src/network/message_network.rs b/src/network/message_network.rs index 9a9447031e..5d96b555f6 100644 --- a/src/network/message_network.rs +++ b/src/network/message_network.rs @@ -18,8 +18,9 @@ //! capabilities //! +use prelude::*; + use io; -use std::borrow::Cow; use network::address::Address; use network::constants::{self, ServiceFlags}; diff --git a/src/network/mod.rs b/src/network/mod.rs index fcf4685161..bf7a05f813 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -20,17 +20,17 @@ use io; use core::fmt; -use std::error; +#[cfg(feature = "std")] use std::error; pub mod constants; -pub mod address; -pub use self::address::Address; -pub mod message; -pub mod message_blockdata; -pub mod message_network; -pub mod message_filter; -pub mod stream_reader; +#[cfg(feature = "std")] pub mod address; +#[cfg(feature = "std")] pub use self::address::Address; +#[cfg(feature = "std")] pub mod message; +#[cfg(feature = "std")] pub mod message_blockdata; +#[cfg(feature = "std")] pub mod message_network; +#[cfg(feature = "std")] pub mod message_filter; +#[cfg(feature = "std")] pub mod stream_reader; /// Network error #[derive(Debug)] @@ -60,8 +60,8 @@ impl From for Error { } } +#[cfg(feature = "std")] impl error::Error for Error { - fn cause(&self) -> Option<&dyn error::Error> { match *self { Error::Io(ref e) => Some(e), diff --git a/src/network/stream_reader.rs b/src/network/stream_reader.rs index ec868215fc..80c53a6a8a 100644 --- a/src/network/stream_reader.rs +++ b/src/network/stream_reader.rs @@ -20,6 +20,8 @@ //! (like can happen with reading from TCP socket) //! +use prelude::*; + use core::fmt; use io::{self, Read}; diff --git a/src/serde_utils.rs b/src/serde_utils.rs index c20fb85397..47e9998a27 100644 --- a/src/serde_utils.rs +++ b/src/serde_utils.rs @@ -7,14 +7,14 @@ pub mod btreemap_byte_values { // NOTE: This module can be exactly copied to use with HashMap. - use ::std::collections::BTreeMap; + use prelude::*; use hashes::hex::{FromHex, ToHex}; use serde; pub fn serialize(v: &BTreeMap>, s: S) -> Result where S: serde::Serializer, - T: serde::Serialize + ::std::hash::Hash + Eq + Ord, + T: serde::Serialize + ::core::hash::Hash + Eq + Ord, { use serde::ser::SerializeMap; @@ -33,17 +33,17 @@ pub mod btreemap_byte_values { pub fn deserialize<'de, D, T>(d: D) -> Result>, D::Error> where D: serde::Deserializer<'de>, - T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + T: serde::Deserialize<'de> + ::core::hash::Hash + Eq + Ord, { - use ::std::marker::PhantomData; + use ::core::marker::PhantomData; struct Visitor(PhantomData); impl<'de, T> serde::de::Visitor<'de> for Visitor where - T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + T: serde::Deserialize<'de> + ::core::hash::Hash + Eq + Ord, { type Value = BTreeMap>; - fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn expecting(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "a map with hexadecimal values") } @@ -74,13 +74,13 @@ pub mod btreemap_as_seq { // NOTE: This module can be exactly copied to use with HashMap. - use ::std::collections::BTreeMap; + use prelude::*; use serde; pub fn serialize(v: &BTreeMap, s: S) -> Result where S: serde::Serializer, - T: serde::Serialize + ::std::hash::Hash + Eq + Ord, + T: serde::Serialize + ::core::hash::Hash + Eq + Ord, U: serde::Serialize, { use serde::ser::SerializeSeq; @@ -100,19 +100,19 @@ pub mod btreemap_as_seq { pub fn deserialize<'de, D, T, U>(d: D) -> Result, D::Error> where D: serde::Deserializer<'de>, - T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + T: serde::Deserialize<'de> + ::core::hash::Hash + Eq + Ord, U: serde::Deserialize<'de>, { - use ::std::marker::PhantomData; + use ::core::marker::PhantomData; struct Visitor(PhantomData<(T, U)>); impl<'de, T, U> serde::de::Visitor<'de> for Visitor where - T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + T: serde::Deserialize<'de> + ::core::hash::Hash + Eq + Ord, U: serde::Deserialize<'de>, { type Value = BTreeMap; - fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn expecting(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "a sequence of pairs") } @@ -143,7 +143,7 @@ pub mod btreemap_as_seq_byte_values { // NOTE: This module can be exactly copied to use with HashMap. - use ::std::collections::BTreeMap; + use prelude::*; use serde; /// A custom key-value pair type that serialized the bytes as hex. @@ -165,7 +165,7 @@ pub mod btreemap_as_seq_byte_values { pub fn serialize(v: &BTreeMap>, s: S) -> Result where S: serde::Serializer, - T: serde::Serialize + ::std::hash::Hash + Eq + Ord + 'static, + T: serde::Serialize + ::core::hash::Hash + Eq + Ord + 'static, { use serde::ser::SerializeSeq; @@ -184,17 +184,17 @@ pub mod btreemap_as_seq_byte_values { pub fn deserialize<'de, D, T>(d: D) -> Result>, D::Error> where D: serde::Deserializer<'de>, - T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + T: serde::Deserialize<'de> + ::core::hash::Hash + Eq + Ord, { - use ::std::marker::PhantomData; + use ::core::marker::PhantomData; struct Visitor(PhantomData); impl<'de, T> serde::de::Visitor<'de> for Visitor where - T: serde::Deserialize<'de> + ::std::hash::Hash + Eq + Ord, + T: serde::Deserialize<'de> + ::core::hash::Hash + Eq + Ord, { type Value = BTreeMap>; - fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn expecting(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "a sequence of pairs") } @@ -239,19 +239,19 @@ pub mod hex_bytes { pub fn deserialize<'de, D, B>(d: D) -> Result where D: serde::Deserializer<'de>, B: serde::Deserialize<'de> + FromHex, { - struct Visitor(::std::marker::PhantomData); + struct Visitor(::core::marker::PhantomData); impl<'de, B: FromHex> serde::de::Visitor<'de> for Visitor { type Value = B; - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { formatter.write_str("an ASCII hex string") } fn visit_bytes(self, v: &[u8]) -> Result where E: serde::de::Error, { - if let Ok(hex) = ::std::str::from_utf8(v) { + if let Ok(hex) = ::core::str::from_utf8(v) { FromHex::from_hex(hex).map_err(E::custom) } else { return Err(E::invalid_value(serde::de::Unexpected::Bytes(v), &self)); @@ -269,7 +269,7 @@ pub mod hex_bytes { if !d.is_human_readable() { serde::Deserialize::deserialize(d) } else { - d.deserialize_str(Visitor(::std::marker::PhantomData)) + d.deserialize_str(Visitor(::core::marker::PhantomData)) } } } diff --git a/src/util/address.rs b/src/util/address.rs index 58777a429f..a3d08dda85 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -33,9 +33,11 @@ //! let address = Address::p2pkh(&public_key, Network::Bitcoin); //! ``` +use prelude::*; + use core::fmt; use core::str::FromStr; -use std::error; +#[cfg(feature = "std")] use std::error; use bech32; use hashes::Hash; @@ -84,8 +86,9 @@ impl fmt::Display for Error { } } -impl error::Error for Error { - fn cause(&self) -> Option<&dyn error::Error> { +#[cfg(feature = "std")] +impl ::std::error::Error for Error { + fn cause(&self) -> Option<&dyn error::Error> { match *self { Error::Base58(ref e) => Some(e), Error::Bech32(ref e) => Some(e), @@ -524,8 +527,8 @@ impl FromStr for Address { } } -impl ::std::fmt::Debug for Address { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl fmt::Debug for Address { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.to_string()) } } diff --git a/src/util/amount.rs b/src/util/amount.rs index f974f44151..777501f064 100644 --- a/src/util/amount.rs +++ b/src/util/amount.rs @@ -14,6 +14,8 @@ //! We refer to the documentation on the types for more information. //! +use prelude::*; + use core::{ops, default, str::FromStr, cmp::Ordering}; use core::fmt::{self, Write}; @@ -111,6 +113,7 @@ impl fmt::Display for ParseAmountError { } } +#[cfg(feature = "std")] impl ::std::error::Error for ParseAmountError {} fn is_too_precise(s: &str, precision: usize) -> bool { @@ -972,8 +975,8 @@ pub mod serde { use serde::{Deserializer, Serializer, de}; use util::amount::serde::SerdeAmountForOpt; - use std::fmt; - use std::marker::PhantomData; + use core::fmt; + use core::marker::PhantomData; pub fn serialize( a: &Option, @@ -1035,8 +1038,8 @@ pub mod serde { use serde::{Deserializer, Serializer, de}; use util::amount::serde::SerdeAmountForOpt; - use std::fmt; - use std::marker::PhantomData; + use core::fmt; + use core::marker::PhantomData; pub fn serialize( a: &Option, @@ -1081,6 +1084,7 @@ pub mod serde { #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "std")] use std::panic; use core::str::FromStr; @@ -1113,7 +1117,11 @@ mod tests { assert_eq!(b, ssat(10)); b %= 3; assert_eq!(b, ssat(1)); + } + #[cfg(feature = "std")] + #[test] + fn test_overflows() { // panic on overflow let result = panic::catch_unwind(|| Amount::max_value() + Amount::from_sat(1)); assert!(result.is_err()); diff --git a/src/util/base58.rs b/src/util/base58.rs index f394616d99..8d2c7279f3 100644 --- a/src/util/base58.rs +++ b/src/util/base58.rs @@ -14,7 +14,8 @@ //! Base58 encoder and decoder -use std::error; +use prelude::*; + use core::{fmt, str, iter, slice}; use hashes::{sha256d, Hash}; @@ -57,7 +58,8 @@ impl fmt::Display for Error { } } -impl error::Error for Error {} +#[cfg(feature = "std")] +impl ::std::error::Error for Error {} /// Vector-like object that holds the first 100 elements on the stack. If more space is needed it /// will be allocated on the heap. diff --git a/src/util/bip143.rs b/src/util/bip143.rs index 7e1087516c..963a3c3b11 100644 --- a/src/util/bip143.rs +++ b/src/util/bip143.rs @@ -25,6 +25,8 @@ use blockdata::script::Script; use blockdata::transaction::{Transaction, TxIn, SigHashType}; use consensus::{encode, Encodable}; +use prelude::*; + use io; use core::ops::{Deref, DerefMut}; diff --git a/src/util/bip158.rs b/src/util/bip158.rs index 58bdf20160..33eecca52d 100644 --- a/src/util/bip158.rs +++ b/src/util/bip158.rs @@ -45,7 +45,8 @@ //! ``` //! -use std::collections::HashSet; +use prelude::*; + use io::{self as io, Cursor}; use core::fmt::{self, Display, Formatter}; use core::cmp::{self, Ordering}; @@ -73,6 +74,7 @@ pub enum Error { Io(io::Error), } +#[cfg(feature = "std")] impl ::std::error::Error for Error {} impl Display for Error { @@ -123,14 +125,14 @@ impl BlockFilter { /// Compute a SCRIPT_FILTER that contains spent and output scripts pub fn new_script_filter(block: &Block, script_for_coin: M) -> Result where M: Fn(&OutPoint) -> Result { - let mut out = Cursor::new(Vec::new()); + let mut out = Vec::new(); { let mut writer = BlockFilterWriter::new(&mut out, block); writer.add_output_scripts(); writer.add_input_scripts(script_for_coin)?; writer.finish()?; } - Ok(BlockFilter { content: out.into_inner() }) + Ok(BlockFilter { content: out }) } /// match any query pattern @@ -361,9 +363,9 @@ impl<'a> GCSFilterWriter<'a> { mapped.sort(); // write number of elements as varint - let mut encoder = io::Cursor::new(Vec::new()); + let mut encoder = Vec::new(); VarInt(mapped.len() as u64).consensus_encode(&mut encoder).unwrap(); - let mut wrote = self.writer.write(encoder.into_inner().as_slice())?; + let mut wrote = self.writer.write(encoder.as_slice())?; // write out deltas of sorted values into a Golonb-Rice coded bit stream let mut writer = BitStreamWriter::new(self.writer); @@ -508,8 +510,7 @@ impl<'a> BitStreamWriter<'a> { #[cfg(test)] mod test { - use std::collections::{HashSet, HashMap}; - use std::io::Cursor; + use io::Cursor; use hash_types::BlockHash; use hashes::hex::FromHex; @@ -520,6 +521,7 @@ mod test { use self::serde_json::{Value}; use consensus::encode::deserialize; + use std::collections::HashMap; #[test] fn test_blockfilters() { @@ -607,7 +609,7 @@ mod test { patterns.insert(Vec::from_hex("eeeeee").unwrap()); patterns.insert(Vec::from_hex("ffffff").unwrap()); - let mut out = Cursor::new(Vec::new()); + let mut out = Vec::new(); { let mut writer = GCSFilterWriter::new(&mut out, 0, 0, M, P); for p in &patterns { @@ -616,7 +618,7 @@ mod test { writer.finish().unwrap(); } - let bytes = out.into_inner(); + let bytes = out; { let mut query = Vec::new(); @@ -659,7 +661,7 @@ mod test { #[test] fn test_bit_stream() { - let mut out = Cursor::new(Vec::new()); + let mut out = Vec::new(); { let mut writer = BitStreamWriter::new(&mut out); writer.write(0, 1).unwrap(); // 0 @@ -671,7 +673,7 @@ mod test { writer.write(7, 7).unwrap(); // 0000111 writer.flush().unwrap(); } - let bytes = out.into_inner(); + let bytes = out; assert_eq!("01011010110000110000000001110000", format!("{:08b}{:08b}{:08b}{:08b}", bytes[0], bytes[1], bytes[2], bytes[3])); { let mut input = Cursor::new(bytes); diff --git a/src/util/bip32.rs b/src/util/bip32.rs index cad830f385..1e663aabd2 100644 --- a/src/util/bip32.rs +++ b/src/util/bip32.rs @@ -16,8 +16,10 @@ //! Implementation of BIP32 hierarchical deterministic wallets, as defined //! at +use prelude::*; + use core::{fmt, str::FromStr, default::Default}; -use std::error; +#[cfg(feature = "std")] use std::error; #[cfg(feature = "serde")] use serde; use hash_types::XpubIdentifier; @@ -274,7 +276,7 @@ impl ::core::iter::FromIterator for DerivationPath { impl<'a> ::core::iter::IntoIterator for &'a DerivationPath { type Item = &'a ChildNumber; - type IntoIter = ::std::slice::Iter<'a, ChildNumber>; + type IntoIter = slice::Iter<'a, ChildNumber>; fn into_iter(self) -> Self::IntoIter { self.0.iter() } @@ -457,6 +459,7 @@ impl fmt::Display for Error { } } +#[cfg(feature = "std")] impl error::Error for Error { fn cause(&self) -> Option<&dyn error::Error> { if let Error::Ecdsa(ref e) = *self { diff --git a/src/util/contracthash.rs b/src/util/contracthash.rs index 74ea12985b..29e0e90547 100644 --- a/src/util/contracthash.rs +++ b/src/util/contracthash.rs @@ -20,8 +20,10 @@ #![cfg_attr(not(test), deprecated)] +use prelude::*; + use core::fmt; -use std::error; +#[cfg(feature = "std")] use std::error; use secp256k1::{self, Secp256k1}; use PrivateKey; @@ -72,6 +74,7 @@ impl fmt::Display for Error { } } +#[cfg(feature = "std")] impl ::std::error::Error for Error { fn cause(&self) -> Option<&dyn error::Error> { match *self { diff --git a/src/util/ecdsa.rs b/src/util/ecdsa.rs index 51d127edbb..9e8589f85b 100644 --- a/src/util/ecdsa.rs +++ b/src/util/ecdsa.rs @@ -16,6 +16,8 @@ //! ECDSA keys used in Bitcoin that can be roundtrip (de)serialized. //! +use prelude::*; + use core::{ops, str::FromStr}; use core::fmt::{self, Write as _fmtWrite}; use io; @@ -100,7 +102,17 @@ impl PublicKey { }; reader.read_exact(&mut bytes[1..])?; - Self::from_slice(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + Self::from_slice(bytes).map_err(|e|{ + // Need a static string for core2 + #[cfg(feature = "std")] + let reason = e; + #[cfg(not(feature = "std"))] + let reason = match e { + Error::Base58(_) => "base58 error", + Error::Secp256k1(_) => "secp256k1 error", + }; + io::Error::new(io::ErrorKind::InvalidData, reason) + }) } /// Serialize the public key to bytes @@ -299,7 +311,7 @@ impl<'de> ::serde::Deserialize<'de> for PrivateKey { impl<'de> ::serde::de::Visitor<'de> for WifVisitor { type Value = PrivateKey; - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { formatter.write_str("an ASCII WIF string") } @@ -307,7 +319,7 @@ impl<'de> ::serde::Deserialize<'de> for PrivateKey { where E: ::serde::de::Error, { - if let Ok(s) = ::std::str::from_utf8(v) { + if let Ok(s) = ::core::str::from_utf8(v) { PrivateKey::from_str(s).map_err(E::custom) } else { Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) @@ -350,7 +362,7 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey { impl<'de> ::serde::de::Visitor<'de> for HexVisitor { type Value = PublicKey; - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { formatter.write_str("an ASCII hex string") } @@ -358,7 +370,7 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey { where E: ::serde::de::Error, { - if let Ok(hex) = ::std::str::from_utf8(v) { + if let Ok(hex) = ::core::str::from_utf8(v) { PublicKey::from_str(hex).map_err(E::custom) } else { Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) @@ -379,7 +391,7 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey { impl<'de> ::serde::de::Visitor<'de> for BytesVisitor { type Value = PublicKey; - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { formatter.write_str("a bytestring") } diff --git a/src/util/hash.rs b/src/util/hash.rs index 35c0160467..3f1080976f 100644 --- a/src/util/hash.rs +++ b/src/util/hash.rs @@ -15,6 +15,8 @@ //! //! Utility functions related to hashing data, including merkleization +use prelude::*; + use io; use core::cmp::min; diff --git a/src/util/key.rs b/src/util/key.rs index 52d73c222b..fd2cba2841 100644 --- a/src/util/key.rs +++ b/src/util/key.rs @@ -20,7 +20,7 @@ pub use util::ecdsa::{PrivateKey, PublicKey}; use core::fmt; -use std::error; +#[cfg(feature = "std")] use std::error; use secp256k1; use util::base58; @@ -44,7 +44,8 @@ impl fmt::Display for Error { } } -impl error::Error for Error { +#[cfg(feature = "std")] +impl ::std::error::Error for Error { fn cause(&self) -> Option<&dyn error::Error> { match *self { Error::Base58(ref e) => Some(e), diff --git a/src/util/merkleblock.rs b/src/util/merkleblock.rs index cc0f0602f4..4e28f2a0f3 100644 --- a/src/util/merkleblock.rs +++ b/src/util/merkleblock.rs @@ -51,7 +51,8 @@ //! assert_eq!(1, index.len()); //! assert_eq!(1, index[0]); //! ``` -use std::collections::HashSet; +use prelude::*; + use io; use hashes::Hash; @@ -392,10 +393,10 @@ pub struct MerkleBlock { } impl MerkleBlock { - /// Create a MerkleBlock from a block, that should contain proofs for the txids. + /// Create a MerkleBlock from a block, that contains proofs for specific txids. /// /// The `block` is a full block containing the header and transactions and `match_txids` is a - /// set containing the transaction ids that should be included in the partial merkle tree. + /// function that returns true for the ids that should be included in the partial merkle tree. /// /// # Examples /// @@ -420,8 +421,8 @@ impl MerkleBlock { /// // Create a merkle block containing a single transaction /// let txid = Txid::from_hex( /// "5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2").unwrap(); - /// let match_txids = vec![txid].into_iter().collect(); - /// let mb = MerkleBlock::from_block(&block, &match_txids); + /// let match_txids: Vec = vec![txid].into_iter().collect(); + /// let mb = MerkleBlock::from_block_with_predicate(&block, |t| match_txids.contains(t)); /// /// // Authenticate and extract matched transaction ids /// let mut matches: Vec = vec![]; @@ -429,23 +430,31 @@ impl MerkleBlock { /// assert!(mb.extract_matches(&mut matches, &mut index).is_ok()); /// assert_eq!(txid, matches[0]); /// ``` - pub fn from_block(block: &Block, match_txids: &HashSet) -> Self { + pub fn from_block_with_predicate(block: &Block, match_txids: F) -> Self + where F: Fn(&Txid) -> bool { let block_txids: Vec<_> = block.txdata.iter().map(Transaction::txid).collect(); - Self::from_header_txids(&block.header, &block_txids, match_txids) + Self::from_header_txids_with_predicate(&block.header, &block_txids, match_txids) } - /// Create a MerkleBlock from the block's header and txids, that should contain proofs for match_txids. + /// Create a MerkleBlock from a block, that contains proofs for specific txids. + #[cfg(feature = "std")] + #[deprecated(since="0.26.2", note="use from_block_with_predicate")] + pub fn from_block(block: &Block, match_txids: &::std::collections::HashSet) -> Self { + Self::from_block_with_predicate(block, |t| match_txids.contains(t)) + } + + /// Create a MerkleBlock from the block's header and txids, that contain proofs for specific txids. /// /// The `header` is the block header, `block_txids` is the full list of txids included in the block and - /// `match_txids` is a set containing the transaction ids that should be included in the partial merkle tree. - pub fn from_header_txids( + /// `match_txids` is a function that returns true for the ids that should be included in the partial merkle tree. + pub fn from_header_txids_with_predicate( header: &BlockHeader, block_txids: &[Txid], - match_txids: &HashSet, - ) -> Self { + match_txids: F, + ) -> Self where F: Fn(&Txid) -> bool { let matches: Vec = block_txids .iter() - .map(|txid| match_txids.contains(txid)) + .map(match_txids) .collect(); let pmt = PartialMerkleTree::from_txids(&block_txids, &matches); @@ -455,6 +464,17 @@ impl MerkleBlock { } } + /// Create a MerkleBlock from the block's header and txids, that should contain proofs for match_txids. + #[cfg(feature = "std")] + #[deprecated(since="0.26.2", note="use from_header_txids_with_predicate")] + pub fn from_header_txids( + header: &BlockHeader, + block_txids: &[Txid], + match_txids: &::std::collections::HashSet, + ) -> Self { + Self::from_header_txids_with_predicate(header, block_txids, |t| match_txids.contains(t)) + } + /// Extract the matching txid's represented by this partial merkle tree /// and their respective indices within the partial tree. /// returns Ok(()) on success, or error in case of failure @@ -642,9 +662,9 @@ mod tests { let txid1 = txids[0]; let txid2 = txids[1]; - let txids = txids.into_iter().collect(); + let txids = vec![txid1, txid2]; - let merkle_block = MerkleBlock::from_block(&block, &txids); + let merkle_block = MerkleBlock::from_block_with_predicate(&block, |t| txids.contains(t)); assert_eq!(merkle_block.header.block_hash(), block.block_hash()); @@ -672,12 +692,12 @@ mod tests { #[test] fn merkleblock_construct_from_txids_not_found() { let block = get_block_13b8a(); - let txids = ["c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"] + let txids: Vec = ["c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"] .iter() .map(|hex| Txid::from_hex(hex).unwrap()) .collect(); - let merkle_block = MerkleBlock::from_block(&block, &txids); + let merkle_block = MerkleBlock::from_block_with_predicate(&block, |t| txids.contains(t)); assert_eq!(merkle_block.header.block_hash(), block.block_hash()); diff --git a/src/util/misc.rs b/src/util/misc.rs index 593daf6848..0009b1713e 100644 --- a/src/util/misc.rs +++ b/src/util/misc.rs @@ -16,6 +16,8 @@ //! //! Various utility functions +use prelude::*; + use hashes::{sha256d, Hash, HashEngine}; use blockdata::opcodes; @@ -29,8 +31,9 @@ pub const BITCOIN_SIGNED_MSG_PREFIX: &[u8] = b"\x18Bitcoin Signed Message:\n"; #[cfg(feature = "secp-recovery")] mod message_signing { + #[cfg(feature = "base64")] use prelude::*; use core::fmt; - use std::error; + #[cfg(feature = "std")] use std::error; use hashes::sha256d; use secp256k1; @@ -60,6 +63,7 @@ mod message_signing { } } + #[cfg(feature = "std")] impl error::Error for MessageSignatureError { fn cause(&self) -> Option<&dyn error::Error> { match *self { diff --git a/src/util/mod.rs b/src/util/mod.rs index 92bfc4ec81..d47cf4a3e3 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -35,8 +35,10 @@ pub mod bip158; pub(crate) mod endian; +use prelude::*; +use io; use core::fmt; -use std::error; +#[cfg(feature = "std")] use std::error; use network; use consensus::encode; @@ -87,8 +89,9 @@ impl fmt::Display for Error { } } -impl error::Error for Error { - fn cause(&self) -> Option<&dyn error::Error> { +#[cfg(feature = "std")] +impl ::std::error::Error for Error { + fn cause(&self) -> Option<&dyn error::Error> { match *self { Error::Encode(ref e) => Some(e), Error::Network(ref e) => Some(e), @@ -110,3 +113,18 @@ impl From for Error { Error::Network(e) } } + +// core2 doesn't have read_to_end +pub(crate) fn read_to_end(mut d: D) -> Result, io::Error> { + let mut result = vec![]; + let mut buf = [0u8; 64]; + loop { + match d.read(&mut buf) { + Ok(0) => break, + Ok(n) => result.extend_from_slice(&buf[0..n]), + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}, + Err(e) => return Err(e.into()), + }; + } + Ok(result) +} \ No newline at end of file diff --git a/src/util/psbt/error.rs b/src/util/psbt/error.rs index 5b0f6bd7fc..67d46de2c1 100644 --- a/src/util/psbt/error.rs +++ b/src/util/psbt/error.rs @@ -12,7 +12,8 @@ // If not, see . // -use std::error; +use prelude::*; + use core::fmt; use blockdata::transaction::Transaction; @@ -105,7 +106,8 @@ impl fmt::Display for Error { } } -impl error::Error for Error {} +#[cfg(feature = "std")] +impl ::std::error::Error for Error {} #[doc(hidden)] impl From for Error { diff --git a/src/util/psbt/macros.rs b/src/util/psbt/macros.rs index f4366e1320..12d82a4195 100644 --- a/src/util/psbt/macros.rs +++ b/src/util/psbt/macros.rs @@ -14,7 +14,7 @@ #[allow(unused_macros)] macro_rules! hex_psbt { - ($s:expr) => { $crate::consensus::deserialize::<$crate::util::psbt::PartiallySignedTransaction>(& as $crate::hashes::hex::FromHex>::from_hex($s).unwrap()) }; + ($s:expr) => { $crate::consensus::deserialize::<$crate::util::psbt::PartiallySignedTransaction>(&<$crate::prelude::Vec as $crate::hashes::hex::FromHex>::from_hex($s).unwrap()) }; } macro_rules! merge { @@ -45,7 +45,7 @@ macro_rules! impl_psbt_deserialize { macro_rules! impl_psbt_serialize { ($thing:ty) => { impl $crate::util::psbt::serialize::Serialize for $thing { - fn serialize(&self) -> Vec { + fn serialize(&self) -> $crate::prelude::Vec { $crate::consensus::serialize(self) } } @@ -118,11 +118,11 @@ macro_rules! impl_psbt_insert_pair { if !$raw_key.key.is_empty() { let key_val: $keyed_key_type = $crate::util::psbt::serialize::Deserialize::deserialize(&$raw_key.key)?; match $slf.$keyed_name.entry(key_val) { - ::std::collections::btree_map::Entry::Vacant(empty_key) => { + $crate::prelude::btree_map::Entry::Vacant(empty_key) => { let val: $keyed_value_type = $crate::util::psbt::serialize::Deserialize::deserialize(&$raw_value)?; empty_key.insert(val); } - ::std::collections::btree_map::Entry::Occupied(_) => return Err($crate::util::psbt::Error::DuplicateKey($raw_key).into()), + $crate::prelude::btree_map::Entry::Occupied(_) => return Err($crate::util::psbt::Error::DuplicateKey($raw_key).into()), } } else { return Err($crate::util::psbt::Error::InvalidKey($raw_key).into()); @@ -180,7 +180,7 @@ macro_rules! impl_psbt_hash_deserialize { macro_rules! impl_psbt_hash_serialize { ($hash_type:ty) => { impl $crate::util::psbt::serialize::Serialize for $hash_type { - fn serialize(&self) -> Vec { + fn serialize(&self) -> $crate::prelude::Vec { self.into_inner().to_vec() } } diff --git a/src/util/psbt/map/global.rs b/src/util/psbt/map/global.rs index 25dc8f5228..42d856ca4a 100644 --- a/src/util/psbt/map/global.rs +++ b/src/util/psbt/map/global.rs @@ -12,8 +12,8 @@ // If not, see . // -use std::collections::BTreeMap; -use std::collections::btree_map::Entry; +use prelude::*; + use io::{self, Cursor, Read}; use core::cmp; @@ -89,12 +89,12 @@ impl Map for Global { match raw_key.type_value { PSBT_GLOBAL_UNSIGNED_TX => return Err(Error::DuplicateKey(raw_key).into()), PSBT_GLOBAL_PROPRIETARY => match self.proprietary.entry(raw::ProprietaryKey::from_key(raw_key.clone())?) { - Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, - Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()), + btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, + btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()), } _ => match self.unknown.entry(raw_key) { - Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, - Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()), + btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, + btree_map::Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()), } } @@ -183,10 +183,10 @@ impl Map for Global { // Merging xpubs for (xpub, (fingerprint1, derivation1)) in other.xpub { match self.xpub.entry(xpub) { - Entry::Vacant(entry) => { + btree_map::Entry::Vacant(entry) => { entry.insert((fingerprint1, derivation1)); }, - Entry::Occupied(mut entry) => { + btree_map::Entry::Occupied(mut entry) => { // Here in case of the conflict we select the version with algorithm: // 1) if everything is equal we do nothing // 2) report an error if @@ -215,7 +215,7 @@ impl Map for Global { } return Err(psbt::Error::MergeConflict(format!( "global xpub {} has inconsistent key sources", xpub - ).to_owned())); + ))); } } } @@ -321,12 +321,12 @@ impl Decodable for Global { } } PSBT_GLOBAL_PROPRIETARY => match proprietary.entry(raw::ProprietaryKey::from_key(pair.key.clone())?) { - Entry::Vacant(empty_key) => {empty_key.insert(pair.value);}, - Entry::Occupied(_) => return Err(Error::DuplicateKey(pair.key).into()), + btree_map::Entry::Vacant(empty_key) => {empty_key.insert(pair.value);}, + btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(pair.key).into()), } _ => match unknowns.entry(pair.key) { - Entry::Vacant(empty_key) => {empty_key.insert(pair.value);}, - Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()), + btree_map::Entry::Vacant(empty_key) => {empty_key.insert(pair.value);}, + btree_map::Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()), } } } diff --git a/src/util/psbt/map/input.rs b/src/util/psbt/map/input.rs index 65dcaf2563..04b0c6e4f6 100644 --- a/src/util/psbt/map/input.rs +++ b/src/util/psbt/map/input.rs @@ -12,8 +12,9 @@ // If not, see . // +use prelude::*; + use io; -use std::collections::btree_map::{BTreeMap, Entry}; use blockdata::script::Script; use blockdata::transaction::{SigHashType, Transaction, TxOut}; @@ -177,14 +178,14 @@ impl Map for Input { psbt_insert_hash_pair(&mut self.hash256_preimages, raw_key, raw_value, error::PsbtHash::Hash256)?; } PSBT_IN_PROPRIETARY => match self.proprietary.entry(raw::ProprietaryKey::from_key(raw_key.clone())?) { - ::std::collections::btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, - ::std::collections::btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()), + btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, + btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()), } _ => match self.unknown.entry(raw_key) { - Entry::Vacant(empty_key) => { + btree_map::Entry::Vacant(empty_key) => { empty_key.insert(raw_value); } - Entry::Occupied(k) => { + btree_map::Entry::Occupied(k) => { return Err(Error::DuplicateKey(k.key().clone()).into()) } }, @@ -307,7 +308,7 @@ where } let key_val: H = Deserialize::deserialize(&raw_key.key)?; match map.entry(key_val) { - Entry::Vacant(empty_key) => { + btree_map::Entry::Vacant(empty_key) => { let val: Vec = Deserialize::deserialize(&raw_value)?; if ::hash(&val) != key_val { return Err(psbt::Error::InvalidPreimageHashPair { @@ -320,6 +321,6 @@ where empty_key.insert(val); Ok(()) } - Entry::Occupied(_) => return Err(psbt::Error::DuplicateKey(raw_key).into()), + btree_map::Entry::Occupied(_) => return Err(psbt::Error::DuplicateKey(raw_key).into()), } } diff --git a/src/util/psbt/map/mod.rs b/src/util/psbt/map/mod.rs index 427088288b..1326137811 100644 --- a/src/util/psbt/map/mod.rs +++ b/src/util/psbt/map/mod.rs @@ -12,6 +12,8 @@ // If not, see . // +use prelude::*; + use io; use consensus::encode; diff --git a/src/util/psbt/map/output.rs b/src/util/psbt/map/output.rs index 18a9272d9a..96978ab414 100644 --- a/src/util/psbt/map/output.rs +++ b/src/util/psbt/map/output.rs @@ -12,9 +12,9 @@ // If not, see . // +use prelude::*; + use io; -use std::collections::BTreeMap; -use std::collections::btree_map::Entry; use blockdata::script::Script; use consensus::encode; @@ -79,12 +79,12 @@ impl Map for Output { } } PSBT_OUT_PROPRIETARY => match self.proprietary.entry(raw::ProprietaryKey::from_key(raw_key.clone())?) { - Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, - Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key.clone()).into()), + btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, + btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key.clone()).into()), } _ => match self.unknown.entry(raw_key) { - Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, - Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()), + btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);}, + btree_map::Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()), } } diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index 94e27720ce..7f315449b3 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -23,6 +23,8 @@ use blockdata::transaction::Transaction; use consensus::{encode, Encodable, Decodable}; use consensus::encode::MAX_VEC_SIZE; +use prelude::*; + use io; mod error; @@ -95,8 +97,8 @@ impl PartiallySignedTransaction { #[cfg(feature = "base64")] mod display_from_str { use super::PartiallySignedTransaction; - use std::fmt::{Display, Formatter, self}; - use std::str::FromStr; + use core::fmt::{Display, Formatter, self}; + use core::str::FromStr; use consensus::encode::{Error, self}; use ::base64::display::Base64Display; @@ -118,6 +120,7 @@ mod display_from_str { } } + #[cfg(feature = "std")] impl ::std::error::Error for PsbtParseError { } impl Display for PartiallySignedTransaction { @@ -215,7 +218,6 @@ mod tests { use hashes::{sha256, hash160, Hash, ripemd160}; use hash_types::Txid; - use std::collections::BTreeMap; use secp256k1::Secp256k1; @@ -230,6 +232,7 @@ mod tests { use super::PartiallySignedTransaction; use util::psbt::raw::ProprietaryKey; + use std::collections::BTreeMap; #[test] fn trivial_psbt() { @@ -464,7 +467,6 @@ mod tests { } mod bip_vectors { - use std::collections::BTreeMap; #[cfg(feature = "base64")] use std::str::FromStr; @@ -477,6 +479,7 @@ mod tests { use util::psbt::map::{Map, Global, Input, Output}; use util::psbt::raw; use util::psbt::{PartiallySignedTransaction, Error}; + use std::collections::BTreeMap; #[test] #[should_panic(expected = "InvalidMagic")] diff --git a/src/util/psbt/raw.rs b/src/util/psbt/raw.rs index 5af2ff88c2..dbe9922846 100644 --- a/src/util/psbt/raw.rs +++ b/src/util/psbt/raw.rs @@ -17,12 +17,14 @@ //! Raw PSBT key-value pairs as defined at //! . +use prelude::*; use core::fmt; -use io; +use io; use consensus::encode::{self, ReadExt, WriteExt, Decodable, Encodable, VarInt, serialize, deserialize, MAX_VEC_SIZE}; use hashes::hex; use util::psbt::Error; +use util::read_to_end; /// A PSBT key in its raw byte form. #[derive(Debug, PartialEq, Hash, Eq, Clone, Ord, PartialOrd)] @@ -152,9 +154,8 @@ impl Encodable for ProprietaryKey where Subtype: Copy + From Decodable for ProprietaryKey where Subtype: Copy + From + Into { fn consensus_decode(mut d: D) -> Result { let prefix = Vec::::consensus_decode(&mut d)?; - let mut key = vec![]; let subtype = Subtype::from(d.read_u8()?); - d.read_to_end(&mut key)?; + let key = read_to_end(d)?; Ok(ProprietaryKey { prefix, diff --git a/src/util/psbt/serialize.rs b/src/util/psbt/serialize.rs index 7a1b83e666..7cbf6ee757 100644 --- a/src/util/psbt/serialize.rs +++ b/src/util/psbt/serialize.rs @@ -17,6 +17,8 @@ //! Defines traits used for (de)serializing PSBT values into/from raw //! bytes in PSBT key-value pairs. +use prelude::*; + use io; use blockdata::script::Script; diff --git a/src/util/uint.rs b/src/util/uint.rs index a80d3887b3..67645f8598 100644 --- a/src/util/uint.rs +++ b/src/util/uint.rs @@ -450,7 +450,7 @@ macro_rules! construct_uint { fn deserialize>( deserializer: D, ) -> Result { - use ::std::fmt; + use ::core::fmt; use $crate::hashes::hex::FromHex; use $crate::serde::de; struct Visitor; @@ -465,7 +465,7 @@ macro_rules! construct_uint { where E: de::Error, { - let bytes = Vec::from_hex(s) + let bytes = $crate::prelude::Vec::from_hex(s) .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(s), &self))?; $name::from_be_slice(&bytes) .map_err(|_| de::Error::invalid_length(bytes.len() * 2, &self)) @@ -509,6 +509,7 @@ impl ::core::fmt::Display for ParseLengthError { } } +#[cfg(feature = "std")] impl ::std::error::Error for ParseLengthError {} impl Uint256 { From bba57d7d69aaeb1a8d0a9c990ba6c326bf24a396 Mon Sep 17 00:00:00 2001 From: Devrandom Date: Sat, 15 May 2021 17:12:27 +0200 Subject: [PATCH 2/2] Embedded test via qemu --- .github/workflows/rust.yml | 21 ++++++++++ embedded/Cargo.toml | 24 +++++++++++ embedded/README.md | 25 ++++++++++++ embedded/memory.x | 5 +++ embedded/scripts/env.sh | 2 + embedded/scripts/install-deps | 3 ++ embedded/src/main.rs | 76 +++++++++++++++++++++++++++++++++++ 7 files changed, 156 insertions(+) create mode 100644 embedded/Cargo.toml create mode 100644 embedded/README.md create mode 100644 embedded/memory.x create mode 100644 embedded/scripts/env.sh create mode 100755 embedded/scripts/install-deps create mode 100644 embedded/src/main.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0ab90f3114..fea2bd8ff5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -57,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 + diff --git a/embedded/Cargo.toml b/embedded/Cargo.toml new file mode 100644 index 0000000000..f8c870b367 --- /dev/null +++ b/embedded/Cargo.toml @@ -0,0 +1,24 @@ +[package] +authors = ["Riccardo Casatta ", "Dev Random "] +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 diff --git a/embedded/README.md b/embedded/README.md new file mode 100644 index 0000000000..bfc433fe71 --- /dev/null +++ b/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. diff --git a/embedded/memory.x b/embedded/memory.x new file mode 100644 index 0000000000..a07f29cff7 --- /dev/null +++ b/embedded/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 512K + RAM : ORIGIN = 0x20000000, LENGTH = 512K +} diff --git a/embedded/scripts/env.sh b/embedded/scripts/env.sh new file mode 100644 index 0000000000..d72a093292 --- /dev/null +++ b/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" diff --git a/embedded/scripts/install-deps b/embedded/scripts/install-deps new file mode 100755 index 0000000000..d22806ddb5 --- /dev/null +++ b/embedded/scripts/install-deps @@ -0,0 +1,3 @@ +#!/bin/sh + +apt install gcc-arm-none-eabi qemu-system-arm gdb-multiarch diff --git a/embedded/src/main.rs b/embedded/src/main.rs new file mode 100644 index 0000000000..c7cc5bdcf2 --- /dev/null +++ b/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 {} +}