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

Actual no_std support #1028

Merged
merged 2 commits into from
Aug 3, 2021
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
14 changes: 7 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
1.41.0,
# 1.45.2 is MSRV for lightning-net-tokio, lightning-block-sync, and coverage generation
1.45.2,
# 1.47.0 will be the MSRV for no_std builds using hashbrown once core2 is updated
# 1.47.0 will be the MSRV for no-std builds using hashbrown once core2 is updated
1.47.0]
include:
- toolchain: stable
Expand Down Expand Up @@ -87,19 +87,19 @@ jobs:
- name: Test on Rust ${{ matrix.toolchain }} with net-tokio and full code-linking for coverage generation
if: matrix.coverage
run: RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always
- name: Test on no_std bullds Rust ${{ matrix.toolchain }}
- name: Test on no-std bullds Rust ${{ matrix.toolchain }}
if: "matrix.build-no-std && !matrix.coverage"
run: |
cd lightning
cargo test --verbose --color always --no-default-features --features no_std
# check if there is a conflict between no_std and the default std feature
cargo test --verbose --color always --features no_std
cargo test --verbose --color always --no-default-features --features no-std
# check if there is a conflict between no-std and the default std feature
cargo test --verbose --color always --features no-std
cd ..
- name: Test on no_std bullds Rust ${{ matrix.toolchain }} and full code-linking for coverage generation
- name: Test on no-std builds Rust ${{ matrix.toolchain }} and full code-linking for coverage generation
if: "matrix.build-no-std && matrix.coverage"
run: |
cd lightning
RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --no-default-features --features no_std
RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --no-default-features --features no-std
cd ..
- name: Test on Rust ${{ matrix.toolchain }}
if: "! matrix.build-net-tokio"
Expand Down
2 changes: 1 addition & 1 deletion ci/check-compiles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ cargo check
cargo doc
cargo doc --document-private-items
cd fuzz && cargo check --features=stdin_fuzz
cd ../lightning && cargo check --no-default-features --features=no_std
cd ../lightning && cargo check --no-default-features --features=no-std
13 changes: 10 additions & 3 deletions lightning/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,32 @@ max_level_debug = []
unsafe_revoked_tx_signing = []
unstable = []

no_std = ["hashbrown", "bitcoin/no-std"]
no-std = ["hashbrown", "bitcoin/no-std", "core2/alloc"]
std = ["bitcoin/std"]

default = ["std"]

[dependencies]
bitcoin = "0.27"
bitcoin = { version = "0.27", default-features = false, features = ["secp-recovery"] }
# TODO remove this once rust-bitcoin PR #637 is released
secp256k1 = { version = "0.20.2", default-features = false, features = ["alloc"] }

hashbrown = { version = "0.11", optional = true }
hex = { version = "0.3", optional = true }
regex = { version = "0.1.80", optional = true }

core2 = { version = "0.3.0", optional = true, default-features = false }

[dev-dependencies]
hex = "0.3"
regex = "0.1.80"
# TODO remove this once rust-bitcoin PR #637 is released
secp256k1 = { version = "0.20.2", default-features = false, features = ["alloc"] }

[dev-dependencies.bitcoin]
version = "0.27"
features = ["bitcoinconsensus"]
default-features = false
Copy link
Collaborator

Choose a reason for hiding this comment

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

Given we rely on libbitcoinconsensus, do we need this?

Copy link
Member Author

Choose a reason for hiding this comment

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

Are you saying that libbitcoinconsensus is dependent on std anyway, so there's no point in turning it off? shouldn't that library be made no_std?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, yea, I guess it could be made no_std eventually, though I doubt anyone is gonna sit down and fight to get it there.

Copy link
Member Author

Choose a reason for hiding this comment

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

So if I remove default-features = false then cargo test --no-default-features --features no_std fails. I think that's because the bitcoin crate has the std feature on, but this crate doesn't, so there's a mismatch in the bitcoin API signatures.

features = ["bitcoinconsensus", "secp-recovery"]

[package.metadata.docs.rs]
features = ["allow_wallclock_use"] # When https://github.com/rust-lang/rust/issues/43781 complies with our MSVR, we can add nice banners in the docs for the methods behind this feature-gate.
12 changes: 6 additions & 6 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use util::events::Event;

use prelude::*;
use core::{cmp, mem};
use std::io::Error;
use io::{self, Error};
use core::ops::Deref;
use sync::Mutex;

Expand Down Expand Up @@ -88,7 +88,7 @@ pub struct ChannelMonitorUpdate {
pub const CLOSED_CHANNEL_UPDATE_ID: u64 = core::u64::MAX;

impl Writeable for ChannelMonitorUpdate {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
write_ver_prefix!(w, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
self.update_id.write(w)?;
(self.updates.len() as u64).write(w)?;
Expand All @@ -100,7 +100,7 @@ impl Writeable for ChannelMonitorUpdate {
}
}
impl Readable for ChannelMonitorUpdate {
fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let _ver = read_ver_prefix!(r, SERIALIZATION_VERSION);
let update_id: u64 = Readable::read(r)?;
let len: u64 = Readable::read(r)?;
Expand Down Expand Up @@ -293,7 +293,7 @@ struct CounterpartyCommitmentTransaction {
}

impl Writeable for CounterpartyCommitmentTransaction {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
w.write_all(&byte_utils::be64_to_array(self.per_htlc.len() as u64))?;
for (ref txid, ref htlcs) in self.per_htlc.iter() {
w.write_all(&txid[..])?;
Expand All @@ -311,7 +311,7 @@ impl Writeable for CounterpartyCommitmentTransaction {
}
}
impl Readable for CounterpartyCommitmentTransaction {
fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let counterparty_commitment_transaction = {
let per_htlc_len: u64 = Readable::read(r)?;
let mut per_htlc = HashMap::with_capacity(cmp::min(per_htlc_len as usize, MAX_ALLOC_SIZE / 64));
Expand Down Expand Up @@ -2581,7 +2581,7 @@ const MAX_ALLOC_SIZE: usize = 64*1024;

impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
for (BlockHash, ChannelMonitor<Signer>) {
fn read<R: ::std::io::Read>(reader: &mut R, keys_manager: &'a K) -> Result<Self, DecodeError> {
fn read<R: io::Read>(reader: &mut R, keys_manager: &'a K) -> Result<Self, DecodeError> {
macro_rules! unwrap_obj {
($key: expr) => {
match $key {
Expand Down
6 changes: 3 additions & 3 deletions lightning/src/chain/keysinterface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use ln::msgs::UnsignedChannelAnnouncement;

use prelude::*;
use core::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use io::{self, Error};
use ln::msgs::{DecodeError, MAX_VALUE_MSAT};

/// Information about a spendable output to a P2WSH script. See
Expand Down Expand Up @@ -699,7 +699,7 @@ impl Writeable for InMemorySigner {
}

impl Readable for InMemorySigner {
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);

let funding_key = Readable::read(reader)?;
Expand Down Expand Up @@ -1039,7 +1039,7 @@ impl KeysInterface for KeysManager {
}

fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
InMemorySigner::read(&mut std::io::Cursor::new(reader))
InMemorySigner::read(&mut io::Cursor::new(reader))
}

fn sign_invoice(&self, invoice_preimage: Vec<u8>) -> Result<RecoverableSignature, ()> {
Expand Down
11 changes: 6 additions & 5 deletions lightning/src/chain/onchaintx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use util::logger::Logger;
use util::ser::{Readable, ReadableArgs, Writer, Writeable, VecWriter};
use util::byte_utils;

use io;
use prelude::*;
use alloc::collections::BTreeMap;
use core::cmp;
Expand Down Expand Up @@ -94,7 +95,7 @@ impl_writeable_tlv_based_enum!(OnchainEvent,
;);

impl Readable for Option<Vec<Option<(usize, Signature)>>> {
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
match Readable::read(reader)? {
0u8 => Ok(None),
1u8 => {
Expand All @@ -115,7 +116,7 @@ impl Readable for Option<Vec<Option<(usize, Signature)>>> {
}

impl Writeable for Option<Vec<Option<(usize, Signature)>>> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
match self {
&Some(ref vec) => {
1u8.write(writer)?;
Expand Down Expand Up @@ -191,7 +192,7 @@ const SERIALIZATION_VERSION: u8 = 1;
const MIN_SERIALIZATION_VERSION: u8 = 1;

impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
pub(crate) fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
pub(crate) fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);

self.destination_script.write(writer)?;
Expand Down Expand Up @@ -242,7 +243,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
}

impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler<K::Signer> {
fn read<R: ::std::io::Read>(reader: &mut R, keys_manager: &'a K) -> Result<Self, DecodeError> {
fn read<R: io::Read>(reader: &mut R, keys_manager: &'a K) -> Result<Self, DecodeError> {
let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);

let destination_script = Readable::read(reader)?;
Expand Down Expand Up @@ -285,7 +286,7 @@ impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler<K::Signer> {
for _ in 0..locktimed_packages_len {
let locktime = Readable::read(reader)?;
let packages_len: u64 = Readable::read(reader)?;
let mut packages = Vec::with_capacity(cmp::min(packages_len as usize, MAX_ALLOC_SIZE / std::mem::size_of::<PackageTemplate>()));
let mut packages = Vec::with_capacity(cmp::min(packages_len as usize, MAX_ALLOC_SIZE / core::mem::size_of::<PackageTemplate>()));
for _ in 0..packages_len {
packages.push(Readable::read(reader)?);
}
Expand Down
10 changes: 6 additions & 4 deletions lightning/src/chain/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ use util::byte_utils;
use util::logger::Logger;
use util::ser::{Readable, Writer, Writeable};

use io;
use prelude::*;
use core::cmp;
use core::mem;
use core::ops::Deref;
Expand Down Expand Up @@ -395,8 +397,8 @@ impl PackageSolvingData {
PackageSolvingData::RevokedOutput(_) => output_conf_height + 1,
PackageSolvingData::RevokedHTLCOutput(_) => output_conf_height + 1,
PackageSolvingData::CounterpartyOfferedHTLCOutput(_) => output_conf_height + 1,
PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => std::cmp::max(outp.htlc.cltv_expiry, output_conf_height + 1),
PackageSolvingData::HolderHTLCOutput(ref outp) => std::cmp::max(outp.cltv_expiry, output_conf_height + 1),
PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => cmp::max(outp.htlc.cltv_expiry, output_conf_height + 1),
PackageSolvingData::HolderHTLCOutput(ref outp) => cmp::max(outp.cltv_expiry, output_conf_height + 1),
PackageSolvingData::HolderFundingOutput(_) => output_conf_height + 1,
};
absolute_timelock
Expand Down Expand Up @@ -682,7 +684,7 @@ impl PackageTemplate {
}

impl Writeable for PackageTemplate {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
writer.write_all(&byte_utils::be64_to_array(self.inputs.len() as u64))?;
for (ref outpoint, ref rev_outp) in self.inputs.iter() {
outpoint.write(writer)?;
Expand All @@ -699,7 +701,7 @@ impl Writeable for PackageTemplate {
}

impl Readable for PackageTemplate {
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
let inputs_count = <u64 as Readable>::read(reader)?;
let mut inputs: Vec<(BitcoinOutPoint, PackageSolvingData)> = Vec::with_capacity(cmp::min(inputs_count as usize, MAX_ALLOC_SIZE / 128));
for _ in 0..inputs_count {
Expand Down
90 changes: 89 additions & 1 deletion lightning/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,119 @@
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]

#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]

#![cfg_attr(all(any(test, feature = "_test_utils"), feature = "unstable"), feature(test))]
#[cfg(all(any(test, feature = "_test_utils"), feature = "unstable"))] extern crate test;

#[cfg(not(any(feature = "std", feature = "no-std")))]
compile_error!("at least one of the `std` or `no-std` features must be enabled");

#[macro_use]
extern crate alloc;
extern crate bitcoin;
#[cfg(any(test, feature = "std"))]
extern crate core;

#[cfg(any(test, feature = "_test_utils"))] extern crate hex;
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))] extern crate regex;

#[cfg(not(feature = "std"))] extern crate core2;

#[macro_use]
pub mod util;
pub mod chain;
pub mod ln;
pub mod routing;

#[cfg(feature = "std")]
use std::io;
#[cfg(not(feature = "std"))]
use core2::io;

#[cfg(not(feature = "std"))]
mod io_extras {
use core2::io::{self, Read, Write};

/// 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<usize> {
Ok(buf.len())
}

#[inline]
fn flush(&mut self) -> core2::io::Result<()> {
Ok(())
}
}

pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64, io::Error>
where
R: Read,
W: Write,
{
let mut count = 0;
let mut buf = [0u8; 64];

loop {
match reader.read(&mut buf) {
Ok(0) => break,
Ok(n) => { writer.write_all(&buf[0..n])?; count += n as u64; },
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
Err(e) => return Err(e.into()),
};
}
Ok(count)
}

pub fn read_to_end<D: io::Read>(mut d: D) -> Result<alloc::vec::Vec<u8>, 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)
}
}

#[cfg(feature = "std")]
mod io_extras {
pub fn read_to_end<D: ::std::io::Read>(mut d: D) -> Result<Vec<u8>, ::std::io::Error> {
let mut buf = Vec::new();
d.read_to_end(&mut buf)?;
Ok(buf)
}

pub use std::io::{copy, sink};
}

mod prelude {
#[cfg(feature = "hashbrown")]
extern crate hashbrown;

pub use alloc::{vec, vec::Vec, string::String, collections::VecDeque};
pub use alloc::{vec, vec::Vec, string::String, collections::VecDeque, boxed::Box};
#[cfg(not(feature = "hashbrown"))]
pub use std::collections::{HashMap, HashSet, hash_map};
#[cfg(feature = "hashbrown")]
pub use self::hashbrown::{HashMap, HashSet, hash_map};

pub use alloc::borrow::ToOwned;
pub use alloc::string::ToString;
}

#[cfg(feature = "std")]
Expand Down
5 changes: 3 additions & 2 deletions lightning/src/ln/chan_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use bitcoin::secp256k1::{Secp256k1, Signature, Message};
use bitcoin::secp256k1::Error as SecpError;
use bitcoin::secp256k1;

use io;
use prelude::*;
use core::cmp;
use ln::chan_utils;
Expand Down Expand Up @@ -167,7 +168,7 @@ impl CounterpartyCommitmentSecrets {
}

impl Writeable for CounterpartyCommitmentSecrets {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
for &(ref secret, ref idx) in self.old_secrets.iter() {
writer.write_all(secret)?;
writer.write_all(&byte_utils::be64_to_array(*idx))?;
Expand All @@ -177,7 +178,7 @@ impl Writeable for CounterpartyCommitmentSecrets {
}
}
impl Readable for CounterpartyCommitmentSecrets {
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
let mut old_secrets = [([0; 32], 1 << 48); 49];
for &mut (ref mut secret, ref mut idx) in old_secrets.iter_mut() {
*secret = Readable::read(reader)?;
Expand Down
Loading