8 Commits

Author SHA1 Message Date
Luke Parker
f9453b1ef4 Attempt downgrading docker from .28 to .27 2025-08-09 14:32:14 -04:00
Luke Parker
ed9c1a2d84 Tweak how subtrate-client tests waits to connect to the Monero node 2025-08-09 14:15:53 -04:00
Luke Parker
a6ffeabafc Add dedicated error for when amounts aren't representable within a u64
Fixes the issue where _inputs_ could still overflow u64::MAX and cause a panic.
2025-08-09 14:06:27 -04:00
Luke Parker
64e7fa112b Have docker install set host 2025-08-09 13:34:25 -04:00
Luke Parker
48a65285a1 Add uidmap dependency for rootless Docker 2025-08-09 12:49:56 -04:00
Luke Parker
f390426cae if: runner.os == 'Linux', with single quotes 2025-08-09 12:38:58 -04:00
Luke Parker
5dcc500b44 Attempt using rootless Docker in CI via the setup-docker-action
Restores using ubuntu-latest.

Basically, at some point in the last year the existing Docker e2e tests started
failing. I'm unclear if this is an issue with the OS, the docker packages, or
what. This just tries to find a solution.
2025-08-09 12:30:37 -04:00
Luke Parker
8df56dca83 Normalize FROM AS casing in Dockerfiles 2025-08-09 12:20:58 -04:00
19 changed files with 102 additions and 47 deletions

View File

@@ -13,9 +13,6 @@ runs:
sudo apt remove -y --allow-remove-essential -f shim-signed
# This command would fail, due to shim-signed having unmet dependencies, hence its removal
sudo apt remove -y "*qemu*" "*sql*" "*texinfo*" "*imagemagick*"
sudo apt autoremove -y
sudo apt clean
docker system prune -a --volumes
if: runner.os == 'Linux'
- name: Remove unused packages
@@ -47,5 +44,31 @@ runs:
svm install 0.8.25
svm use 0.8.25
- name: Remove preinstalled Docker
shell: bash
run: |
docker system prune -a --volumes
sudo apt remove -y *docker*
# Install uidmap which will be required for the explicitly installed Docker
sudo apt install uidmap
if: runner.os == 'Linux'
- name: Update system dependencies
shell: bash
run: |
sudo apt update -y
sudo apt upgrade -y
sudo apt autoremove -y
sudo apt clean
if: runner.os == 'Linux'
- name: Install rootless Docker
uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19
with:
rootless: true
set-host: true
version: type=image,tag=27.5.1
if: runner.os == 'Linux'
# - name: Cache Rust
# uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43

View File

@@ -29,7 +29,7 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac

View File

@@ -11,7 +11,7 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac

View File

@@ -25,7 +25,7 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac

View File

@@ -18,7 +18,7 @@ on:
jobs:
# Only run these once since they will be consistent regardless of any node
unit-tests:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
@@ -43,7 +43,7 @@ jobs:
# Doesn't run unit tests with features as the tests workflow will
integration-tests:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
# Test against all supported protocol versions
strategy:
matrix:

View File

@@ -19,7 +19,7 @@ on:
jobs:
test-networks:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac

View File

@@ -29,7 +29,7 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac

View File

@@ -25,7 +25,7 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac

View File

@@ -27,7 +27,7 @@ on:
jobs:
test-infra:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
@@ -47,7 +47,7 @@ jobs:
-p serai-docker-tests
test-substrate:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
@@ -77,7 +77,7 @@ jobs:
-p serai-node
test-serai-client:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac

View File

@@ -177,6 +177,17 @@ pub enum SendError {
/// The created transaction was too large.
#[cfg_attr(feature = "std", error("too large of a transaction"))]
TooLargeTransaction,
/// The transactions' amounts could not be represented within a `u64`.
#[cfg_attr(
feature = "std",
error("transaction amounts exceed u64::MAX (in {in_amount}, out {out_amount})")
)]
AmountsUnrepresentable {
/// The amount in (via inputs).
in_amount: u128,
/// The amount which would be out (between outputs and the fee).
out_amount: u128,
},
/// This transaction could not pay for itself.
#[cfg_attr(
feature = "std",
@@ -300,23 +311,34 @@ impl SignableTransaction {
}
// Make sure we have enough funds
let in_amount = self.inputs.iter().map(|input| input.commitment().amount).sum::<u64>();
let payments_amount = self
.payments
.iter()
.filter_map(|payment| match payment {
InternalPayment::Payment(_, amount) => Some(*amount),
InternalPayment::Change(_) => None,
})
.try_fold(0, u64::checked_add);
let payments_amount = payments_amount.ok_or(SendError::TooManyOutputs)?;
let (weight, necessary_fee) = self.weight_and_necessary_fee();
if payments_amount.checked_add(necessary_fee).is_none_or(|total_out| in_amount < total_out) {
Err(SendError::NotEnoughFunds {
inputs: in_amount,
outputs: payments_amount,
necessary_fee: Some(necessary_fee),
})?;
let weight;
{
let in_amount: u128 =
self.inputs.iter().map(|input| u128::from(input.commitment().amount)).sum();
let payments_amount: u128 = self
.payments
.iter()
.filter_map(|payment| match payment {
InternalPayment::Payment(_, amount) => Some(u128::from(*amount)),
InternalPayment::Change(_) => None,
})
.sum();
let necessary_fee;
(weight, necessary_fee) = self.weight_and_necessary_fee();
let out_amount = payments_amount + u128::from(necessary_fee);
let in_out_amount = u64::try_from(in_amount)
.and_then(|in_amount| u64::try_from(out_amount).map(|out_amount| (in_amount, out_amount)));
let Ok((in_amount, out_amount)) = in_out_amount else {
Err(SendError::AmountsUnrepresentable { in_amount, out_amount })?
};
if in_amount < out_amount {
Err(SendError::NotEnoughFunds {
inputs: in_amount,
outputs: u64::try_from(payments_amount)
.expect("total out fit within u64 but not part of total out"),
necessary_fee: Some(necessary_fee),
})?;
}
}
// The limit is half the no-penalty block size

View File

@@ -92,7 +92,7 @@ fn os(os: Os, additional_root: &str, user: &str) -> String {
match os {
Os::Alpine => format!(
r#"
FROM alpine:latest as image
FROM alpine:latest AS image
COPY --from=mimalloc-alpine libmimalloc.so /usr/lib
ENV LD_PRELOAD=libmimalloc.so
@@ -117,7 +117,7 @@ WORKDIR /home/{user}
Os::Debian => format!(
r#"
FROM debian:bookworm-slim as image
FROM debian:bookworm-slim AS image
COPY --from=mimalloc-debian libmimalloc.so /usr/lib
RUN echo "/usr/lib/libmimalloc.so" >> /etc/ld.so.preload
@@ -146,7 +146,7 @@ fn build_serai_service(prelude: &str, release: bool, features: &str, package: &s
format!(
r#"
FROM rust:1.89-slim-bookworm as builder
FROM rust:1.89-slim-bookworm AS builder
COPY --from=mimalloc-debian libmimalloc.so /usr/lib
RUN echo "/usr/lib/libmimalloc.so" >> /etc/ld.so.preload

View File

@@ -2,7 +2,7 @@ use crate::Os;
pub fn mimalloc(os: Os) -> &'static str {
const ALPINE_MIMALLOC: &str = r#"
FROM alpine:latest as mimalloc-alpine
FROM alpine:latest AS mimalloc-alpine
RUN apk update && apk upgrade && apk --no-cache add gcc g++ libc-dev make cmake git
RUN git clone https://github.com/microsoft/mimalloc && \
@@ -16,7 +16,7 @@ RUN git clone https://github.com/microsoft/mimalloc && \
"#;
const DEBIAN_MIMALLOC: &str = r#"
FROM debian:bookworm-slim as mimalloc-debian
FROM debian:bookworm-slim AS mimalloc-debian
RUN apt update && apt upgrade -y && apt install -y gcc g++ make cmake git
RUN git clone https://github.com/microsoft/mimalloc && \

View File

@@ -5,7 +5,7 @@ use crate::{Network, Os, mimalloc, os, write_dockerfile};
pub fn bitcoin(orchestration_path: &Path, network: Network) {
#[rustfmt::skip]
const DOWNLOAD_BITCOIN: &str = r#"
FROM alpine:latest as bitcoin
FROM alpine:latest AS bitcoin
ENV BITCOIN_VERSION=27.1

View File

@@ -5,7 +5,7 @@ pub fn lighthouse(network: Network) -> (String, String, String) {
#[rustfmt::skip]
const DOWNLOAD_LIGHTHOUSE: &str = r#"
FROM alpine:latest as lighthouse
FROM alpine:latest AS lighthouse
ENV LIGHTHOUSE_VERSION=5.1.3

View File

@@ -20,7 +20,7 @@ pub fn nimbus(network: Network) -> (String, String, String) {
#[rustfmt::skip]
let download_nimbus = format!(r#"
FROM alpine:latest as nimbus
FROM alpine:latest AS nimbus
ENV NIMBUS_VERSION=24.3.0
ENV NIMBUS_COMMIT=dc19b082

View File

@@ -5,7 +5,7 @@ pub fn reth(network: Network) -> (String, String, String) {
#[rustfmt::skip]
const DOWNLOAD_RETH: &str = r#"
FROM alpine:latest as reth
FROM alpine:latest AS reth
ENV RETH_VERSION=0.2.0-beta.6

View File

@@ -22,7 +22,7 @@ fn monero_internal(
#[rustfmt::skip]
let download_monero = format!(r#"
FROM alpine:latest as monero
FROM alpine:latest AS monero
RUN apk --no-cache add gnupg

View File

@@ -390,6 +390,8 @@ impl Monero {
MakeSignableTransactionResult::SignableTransaction(signable)
}
})),
// AmountsUnrepresentable is unreachable on Monero without 100% of the supply before tail
// emission or fundamental corruption
Err(e) => match e {
SendError::UnsupportedRctType => {
panic!("trying to use an RctType unsupported by monero-wallet")
@@ -398,6 +400,7 @@ impl Monero {
SendError::InvalidDecoyQuantity |
SendError::NoOutputs |
SendError::TooManyOutputs |
SendError::AmountsUnrepresentable { .. } |
SendError::NoChange |
SendError::TooMuchArbitraryData |
SendError::TooLargeTransaction |

View File

@@ -49,17 +49,24 @@ macro_rules! serai_test {
test.provide_container(composition);
test.run_async(|ops| async move {
// Sleep until the Substrate RPC starts
let serai_rpc = ops.handle(handle).host_port(9944).unwrap();
let serai_rpc = format!("http://{}:{}", serai_rpc.0, serai_rpc.1);
// Bound execution to 60 seconds
for _ in 0 .. 60 {
let mut ticks = 0;
let serai_rpc = loop {
// Bound execution to 60 seconds
if ticks > 60 {
panic!("Serai node didn't start within 60 seconds");
}
tokio::time::sleep(core::time::Duration::from_secs(1)).await;
ticks += 1;
let Ok(serai_rpc) = ops.handle(handle).host_port(9944) else { continue };
let serai_rpc = format!("http://{}:{}", serai_rpc.0, serai_rpc.1);
let Ok(client) = Serai::new(serai_rpc.clone()).await else { continue };
if client.latest_finalized_block_hash().await.is_err() {
continue;
}
break;
}
break serai_rpc;
};
#[allow(clippy::redundant_closure_call)]
$test(Serai::new(serai_rpc).await.unwrap()).await;
}).await;