Skip to content

Commit

Permalink
build: Remove cargo vendor, mount CARGO_HOME from host instead
Browse files Browse the repository at this point in the history
  • Loading branch information
shesek committed Sep 25, 2020
1 parent 5fd7028 commit 1ef06a5
Show file tree
Hide file tree
Showing 4 changed files with 303 additions and 8 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ script:

# Report reproducible builds shasums
- >
docker_mounts="-v `pwd`:/usr/src/bwt -v ${CARGO_HOME:-$HOME/.cargo}:/usr/local/cargo" &&
(docker inspect bwt-builder &> /dev/null || docker build -t bwt-builder -f scripts/builder.Dockerfile .) &&
(docker inspect bwt-builder-osx &> /dev/null || docker build -t bwt-builder-osx -f scripts/builder-osx.Dockerfile .) &&
docker run -v `pwd`:/usr/src/bwt bwt-builder &&
docker run -v `pwd`:/usr/src/bwt bwt-builder-osx &&
docker run $docker_mounts bwt-builder &&
docker run $docker_mounts bwt-builder-osx &&
echo '-----BEGIN SHA256SUM-----' &&
(cd dist && sha256sum *) | sort &&
echo ''
4 changes: 0 additions & 4 deletions scripts/builder.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ RUN apt-get update && apt-get install -y pkg-config libssl-dev gcc-mingw-w64-x86
rustup target add x86_64-pc-windows-gnu

WORKDIR /usr/src/bwt
COPY Cargo.toml Cargo.lock ./
RUN mkdir src .cargo && touch src/lib.rs src/main.rs && \
cargo vendor > .cargo/config

VOLUME /usr/src/bwt
ENV TARGETS=linux,win
ENTRYPOINT [ "/usr/src/bwt/scripts/build.sh" ]
5 changes: 3 additions & 2 deletions scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ if [ -z "$SKIP_BUILD" ]; then
rm -rf dist/*

if [ -z "$BUILD_HOST" ]; then
docker_mounts="-v `pwd`:/usr/src/bwt -v ${CARGO_HOME:-$HOME/.cargo}:/usr/local/cargo"
docker build -t bwt-builder -f scripts/builder.Dockerfile .
docker run -it --rm -e OWNER=`id -u` -v `pwd`:/usr/src/bwt bwt-builder
docker run -it --rm -e OWNER=`id -u` $docker_mounts bwt-builder
if [ -z "$SKIP_OSX" ]; then
docker build -t bwt-builder-osx -f scripts/builder-osx.Dockerfile .
docker run -it --rm -e OWNER=`id -u` -v `pwd`:/usr/src/bwt bwt-builder-osx
docker run -it --rm -e OWNER=`id -u` $docker_mounts bwt-builder-osx
fi
else
# macOS builds are disabled by default when building on the host.
Expand Down
297 changes: 297 additions & 0 deletions src/banner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
use std::time::{Duration as StdDuration, UNIX_EPOCH};

use chrono::Duration;

use bitcoin::{blockdata::constants, Amount};
use bitcoincore_rpc::RpcApi;

use crate::bitcoincore_ext::RpcApiExt;
use crate::util::BoolThen;
use crate::{Query, Result};

const DIFFCHANGE_INTERVAL: u64 = constants::DIFFCHANGE_INTERVAL as u64;
const TARGET_BLOCK_SPACING: u64 = constants::TARGET_BLOCK_SPACING as u64;
const INITIAL_REWARD: u64 = 50 * constants::COIN_VALUE;
const HALVING_INTERVAL: u64 = 210_000;
//const MAX_BLOCK_WEIGHT: f64 = constants::MAX_BLOCK_WEIGHT as f64;

// XXX remove % full?
// XXX network.run_from_another_thread(network.interface.session.send_request("server.banner",[getmpk()]))
// FIXME use widetext directly where possible

pub fn get_welcome_banner(query: &Query, include_supply: bool) -> Result<String> {
let rpc = query.rpc();

let net_info = rpc.get_network_info()?;
let chain_info = rpc.get_blockchain_info()?;
let mempool_info = rpc.get_mempool_info()?;
let net_totals = rpc.get_net_totals()?;
let peers = rpc.get_peer_info()?;
let hash_rate_7d = rpc.get_network_hash_ps(1008)?;
let uptime = dur_from_secs(rpc.uptime()?);
let tip_stats = rpc.get_block_stats(&rpc.get_best_block_hash()?)?;

let est_fee = |target| {
query
.estimate_fee(target)
.ok()
.flatten()
.map_or("ₙ.ₐ.".into(), |rate| format!("{:.1}", rate))
};
let est_20m = est_fee(2u16);
let est_3h = est_fee(18u16);
let est_1d = est_fee(144u16);

//let store_stats = query.store_stats();

let mut chain_name = chain_info.chain;
if chain_name == "main" || chain_name == "test" {
chain_name = format!("{}net", chain_name)
};

let height = chain_info.headers;

// 24 hour average bandwidth usage
let num_days = uptime.num_seconds() as f64 / 86400f64;
let bandwidth_up = (net_totals.total_bytes_sent as f64 / num_days) as u64;
let bandwidth_down = (net_totals.total_bytes_recv as f64 / num_days) as u64;

// Time until the next difficulty adjustment
let retarget_blocks = DIFFCHANGE_INTERVAL - (height % DIFFCHANGE_INTERVAL);
let retarget_dur = dur_from_secs(retarget_blocks * TARGET_BLOCK_SPACING);

// Current reward era and time until next halving
let reward_era = height / HALVING_INTERVAL;
let block_reward = Amount::from_sat(INITIAL_REWARD / 2u64.pow(reward_era as u32));
let halving_blocks = HALVING_INTERVAL - (height % HALVING_INTERVAL);
let halving_dur = dur_from_secs(halving_blocks * TARGET_BLOCK_SPACING);

let tip_ago = match (UNIX_EPOCH + StdDuration::from_secs(tip_stats.time as u64)).elapsed() {
Ok(elapsed) => format!("{} ago", format_dur(&Duration::from_std(elapsed).unwrap())),
Err(_) => format!("just now"), // account for blocks with a timestamp slightly in the future
};

//let tip_full = tip_stats.total_weight as f64 / MAX_BLOCK_WEIGHT * 100f64;

// BTC/kb -> sat/vB
let mempool_min_fee = mempool_info.mempool_min_fee.as_sat() as f64 / 1000f64;

let has_inbound = peers.iter().any(|p| p.inbound);

let modes = [
if chain_info.pruned {
"✂️ ᴘʀᴜɴᴇᴅ"
} else {
"🗄️ ᴀʀᴄʜɪᴠᴀʟ"
},
if net_info.local_relay {
"🗣️ ᴍᴇᴍᴘᴏᴏʟ ʀᴇʟᴀʏ"
} else {
"🖁 ʙʟᴏᴄᴋꜱᴏɴʟʏ"
},
if has_inbound {
"👂 ʟɪꜱᴛᴇɴꜱ"
} else {
"🙉 ɴᴏʟɪꜱᴛᴇɴ"
},
];

let ver_lines = big_numbers(crate::BWT_VERSION);

let (supply_frag, utxo_size_frag) = include_supply
.and_then(|| {
info!("running gettxoutsetinfo to audit the bitcoin supply, this may take a few minutes...");
Some(rpc.get_tx_out_set_info())
})
.transpose()?
.map_or(("".into(), "".into()), |utxo_info| {
let total_supply = utxo_info.total_amount.to_string();
let supply_frag = format!(
r#"
✔️ ᴠᴇʀɪꜰɪᴇᴅ ✔️
ᴄɪʀᴄᴜʟᴀᴛɪɴɢ {total_supply}
ꜱᴜᴘᴘʟʏ {total_supply_line}
"#,
total_supply = to_smallcaps(&total_supply),
total_supply_line = "‾".repeat(total_supply.len()),
);
let utxo_size_frag = format!("\n 𝚄𝚃𝚇𝙾 𝚂𝙸𝚉𝙴: 🗃️ {utxo_size}", utxo_size = to_smallcaps(&format_bytes(utxo_info.disk_size)));
(supply_frag, utxo_size_frag)
});


// ᴛʀᴀᴄᴋɪɴɢ: 🔍 {stat_tx_count} ᴛxꜱ & ᴀᴄᴛɪᴠᴇ ᴀᴅᴅʀᴇꜱꜱᴇꜱ
Ok(format!(
r#"
██████  ██  ██ ████████ 
██   ██ ██  ██    ██    
██████  ██  █  ██  ██  {ver_line1}
██   ██ ██ ███ ██  ██  █ █ {ver_line2}
██████   ███ ███   ██  ▀▄▀ {ver_line3}
{client_name}
{modes}
𝙽𝙴𝚃𝚆𝙾𝚁𝙺: 🌐 {chain_name}
𝙲𝙾𝙽𝙽𝙴𝙲𝚃𝙴𝙳: 🖧 {connected_peers} ᴘᴇᴇʀꜱ
𝚄𝙿𝚃𝙸𝙼𝙴: ⏱️ {uptime}
𝙱𝙰𝙽𝙳𝚆𝙸𝙳𝚃𝙷: 📡 {bandwidth_up} 🔼 {bandwidth_down} 🔽 (24ʜ ᴀᴠɢ)
𝙲𝙷𝙰𝙸𝙽 𝚂𝙸𝚉𝙴: 💾 {chain_size}{utxo_size_frag}
𝙷𝙰𝚂𝙷𝚁𝙰𝚃𝙴: ⛏️ {hash_rate} (7ᴅ ᴀᴠɢ)
𝙳𝙸𝙵𝙵𝙸𝙲𝚄𝙻𝚃𝚈: 🏋️ {difficulty} (ʀᴇ-🎯 ɪɴ {retarget_dur} ⏳)
𝚁𝙴𝚆𝙰𝚁𝙳 𝙴𝚁𝙰: 🎁 {block_reward:.2} ʙᴛᴄ (½ ɪɴ {halving_dur} ⏳)
𝙻𝙰𝚂𝚃 𝙱𝙻𝙾𝙲𝙺: ⛓️ {tip_height} / {tip_ago} / {tip_size} / {tip_n_tx}
ꜰᴇᴇʀᴀᴛᴇ {tip_fee_per10}-{tip_fee_per90} ꜱᴀᴛ/ᴠʙ / ᴀᴠɢ {tip_fee_avg} ꜱᴀᴛ/ᴠʙ / ᴛᴏᴛᴀʟ {tip_fee_total:.3} ʙᴛᴄ
𝙼𝙴𝙼𝙿𝙾𝙾𝙻: 💭 {mempool_size} / {mempool_n_tx} / ᴍɪɴ {mempool_min_fee} ꜱᴀᴛ/ᴠʙ
𝙵𝙴𝙴𝚂 𝙴𝚂𝚃: 🏷️ 20 ᴍɪɴᴜᴛᴇꜱ: {est_20m} / 3 ʜᴏᴜʀꜱ: {est_3h} / 1 ᴅᴀʏ: {est_1d} (ꜱᴀᴛ/ᴠʙ)
{supply_frag}
𝚂𝚄𝙿𝙿𝙾𝚁𝚃 𝙳𝙴𝚅𝙴𝙻𝙾𝙿𝙼𝙴𝙽𝚃: bc1qmuagsjvq0lh3admnafk0qnlql0vvxv08au9l2d / https://btcpay.shesek.info
"#,
client_name = to_widetext(&net_info.subversion),
chain_name = to_smallcaps(&chain_name),
connected_peers = net_info.connections,
uptime = to_smallcaps(&format_dur(&uptime).to_uppercase()),
bandwidth_up = to_smallcaps(&format_bytes(bandwidth_up)),
bandwidth_down = to_smallcaps(&format_bytes(bandwidth_down)),
chain_size = to_smallcaps(&format_bytes(chain_info.size_on_disk)),
utxo_size_frag = utxo_size_frag,
hash_rate = to_smallcaps(&format_metric(hash_rate_7d, " ", "H/s")),
difficulty = to_smallcaps(&format_metric(chain_info.difficulty as f64, " ", "")),
retarget_dur = to_smallcaps(&format_dur(&retarget_dur).to_uppercase()),
halving_dur = to_smallcaps(&format_dur(&halving_dur).to_uppercase()),
block_reward = block_reward.as_btc(),
tip_height = tip_stats.height,
tip_ago = to_smallcaps(&tip_ago),
tip_size = to_smallcaps(&format_bytes(tip_stats.total_size as u64)),
tip_n_tx = to_smallcaps(&format_metric(tip_stats.txs as f64, "", " txs")),
tip_fee_per10 = tip_stats.feerate_percentiles.0,
tip_fee_per90 = tip_stats.feerate_percentiles.4,
tip_fee_avg = tip_stats.avg_fee_rate,
tip_fee_total = tip_stats.total_fee.as_btc(),
//tip_full = tip_full, / {tip_full:.1}% ꜰᴜʟʟ
mempool_size = to_smallcaps(&format_bytes(mempool_info.bytes)),
mempool_n_tx = to_smallcaps(&format_metric(mempool_info.size as f64, "", " txs")),
mempool_min_fee = mempool_min_fee,
est_20m = est_20m,
est_3h = est_3h,
est_1d = est_1d,
modes = modes.join(" "),
ver_line1 = ver_lines.0,
ver_line2 = ver_lines.1,
ver_line3 = ver_lines.2,
supply_frag = supply_frag,
))
}

fn dur_from_secs(seconds: u64) -> Duration {
Duration::from_std(StdDuration::from_secs(seconds)).unwrap()
}

fn format_dur(dur: &Duration) -> String {
let days = dur.num_days();
if days > 90 {
return format!("{} months", days / 30);
}
if days > 21 {
return format!("{} weeks", days / 7);
}
if days > 3 {
return format!("{} days", days);
}
let hours = dur.num_hours();
if hours > 3 {
return format!("{} hours", hours);
}
let minutes = dur.num_minutes();
if minutes > 3 {
return format!("{} minutes", minutes);
}
format!("{} seconds", dur.num_seconds())
}

fn format_bytes(bytes: u64) -> String {
format_metric(bytes as f64, " ", "B")
}

fn format_metric(num: f64, space: &str, suf: &str) -> String {
if num >= 1000000000000000000f64 {
format!("{:.1}{}E{}", num / 1000000000000000000f64, space, suf)
} else if num >= 1000000000000000f64 {
format!("{:.1}{}P{}", num / 1000000000000000f64, space, suf)
} else if num >= 1000000000000f64 {
format!("{:.1}{}T{}", num / 1000000000000f64, space, suf)
} else if num >= 1000000000f64 {
format!("{:.1}{}G{}", num / 1000000000f64, space, suf)
} else if num >= 1000000f64 {
format!("{:.1}{}M{}", num / 1000000f64, space, suf)
} else if num >= 1000f64 {
format!("{:.1}{}K{}", num / 1000f64, space, suf)
} else {
format!("{:.1}{}{}", num, space, suf)
}
}

lazy_static! {
static ref SMALLCAPS_ALPHABET: Vec<char> =
"ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘQʀꜱᴛᴜᴠᴡxʏᴢᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘQʀꜱᴛᴜᴠᴡxʏᴢ01234567890./:".chars().collect::<Vec<_>>();
static ref WIDETEXT_ALPHABET: Vec<char> =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890./:"
.chars()
.collect::<Vec<_>>();
}

fn convert_alphabet(s: &str, alphabet: &[char]) -> String {
s.chars()
.map(|c| match c {
'a'..='z' => alphabet[c as usize - 97],
'A'..='Z' => alphabet[c as usize - 65 + 26],
'0'..='9' => alphabet[c as usize - 48 + 26 * 2],
'.' => alphabet[63],
'/' => alphabet[64],
':' => alphabet[65],
c => c,
})
.collect()
}

fn to_smallcaps(s: &str) -> String {
convert_alphabet(s, &SMALLCAPS_ALPHABET[..])
}
fn to_widetext(s: &str) -> String {
convert_alphabet(s, &WIDETEXT_ALPHABET[..])
}

fn big_numbers(s: &str) -> (String, String, String) {
let mut lines = ("".to_string(), "".to_string(), "".to_string());
for c in s.chars() {
let char_lines = match c {
'0' => ("█▀▀█", "█ █", "█▄▄█"),
'1' => ("▄█ ", " █ ", "▄█▄"),
'2' => ("█▀█", " ▄▀", "█▄▄"),
'3' => ("█▀▀█", " ▀▄", "█▄▄█"),
'4' => (" █▀█ ", "█▄▄█▄", " █ "),
'5' => ("█▀▀", "▀▀▄", "▄▄▀"),
'6' => ("▄▀▀▄", "█▄▄ ", "▀▄▄▀"),
'7' => ("▀▀▀█", " █ ", " ▐▌ "),
'8' => ("▄▀▀▄", "▄▀▀▄", "▀▄▄▀"),
'9' => ("▄▀▀▄", "▀▄▄█", " ▄▄▀"),
'.' => (" ", " ", "█"),
_ => continue,
};
lines.0.push_str(char_lines.0);
lines.1.push_str(char_lines.1);
lines.2.push_str(char_lines.2);
lines.0.push_str(" ");
lines.1.push_str(" ");
lines.2.push_str(" ");
}
lines
}

0 comments on commit 1ef06a5

Please sign in to comment.