Skip to content

Commit f06116e

Browse files
committed
feat: build wasm in CI to ensure compatibility
resolves arkade-os#62 fix: clippy fix: install llvm and run on macos fix: install llvm using homebrew chore: extra step for macos chore: update path fix: now it works right? :) chore: make it run in ci
1 parent b5852f3 commit f06116e

File tree

6 files changed

+254
-10
lines changed

6 files changed

+254
-10
lines changed

.github/workflows/ci.yml

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,57 @@ jobs:
5252
- uses: Swatinem/rust-cache@v2
5353
- run: cargo clippy --all-targets --all-features -- -D warnings
5454

55-
wasm:
56-
runs-on: ubuntu-latest
55+
wasm_ubuntu:
56+
strategy:
57+
fail-fast: false
58+
matrix:
59+
os: [ ubuntu-latest ]
60+
runs-on: ${{ matrix.os }}
5761
steps:
5862
- uses: actions/checkout@v4
5963
- uses: extractions/setup-just@v2
6064
- uses: dtolnay/rust-toolchain@master
6165
with:
6266
toolchain: stable
6367
targets: wasm32-unknown-unknown
68+
- name: Install wasm-pack
69+
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
6470
- uses: Swatinem/rust-cache@v2
6571
- name: Build crates for WASM
6672
env:
6773
CC: gcc
6874
run: just build-wasm
75+
- name: Run wasm test
76+
env:
77+
CC: gcc
78+
run: just test-wasm
79+
80+
wasm_macos:
81+
strategy:
82+
fail-fast: false
83+
matrix:
84+
os: [ macos-latest ]
85+
runs-on: ${{ matrix.os }}
86+
steps:
87+
- uses: actions/checkout@v4
88+
- uses: extractions/setup-just@v2
89+
- uses: dtolnay/rust-toolchain@master
90+
with:
91+
toolchain: stable
92+
targets: wasm32-unknown-unknown
93+
- name: Install wasm-pack
94+
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
95+
96+
- name: Install LLVM and Clang
97+
if: contains(matrix.os, 'macos')
98+
run: brew install llvm
99+
- uses: Swatinem/rust-cache@v2
100+
- name: Build crates for WASM
101+
run: PATH="/opt/homebrew/opt/llvm/bin:$PATH" cargo build -p ark-core --target wasm32-unknown-unknown
102+
- name: Run wasm test
103+
run: |
104+
cd ark-rest
105+
PATH="/opt/homebrew/opt/llvm/bin:$PATH" wasm-pack test --node
69106
70107
unit-tests:
71108
runs-on: ubuntu-latest

.github/workflows/wasm.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: WASM Tests
2+
3+
on:
4+
pull_request:
5+
merge_group:
6+
7+
env:
8+
CARGO_INCREMENTAL: 0
9+
CARGO_TERM_COLOR: always
10+
ARK_GO_DIR: ./ark-go
11+
ARKD_DIR: ./ark-go/ark/server
12+
13+
permissions:
14+
actions: read
15+
contents: read
16+
17+
jobs:
18+
19+
wasm_ubuntu:
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
os: [ ubuntu-latest ]
24+
arkd-version: [ 'v0.5.9' ]
25+
runs-on: ${{ matrix.os }}
26+
steps:
27+
- uses: actions/checkout@v4
28+
- uses: extractions/setup-just@v2
29+
- uses: dtolnay/rust-toolchain@master
30+
with:
31+
toolchain: stable
32+
targets: wasm32-unknown-unknown
33+
- name: Install wasm-pack
34+
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
35+
- uses: Swatinem/rust-cache@v2
36+
37+
- name: Build crates for WASM
38+
run: just build-wasm
39+
40+
wasm_macos:
41+
strategy:
42+
fail-fast: false
43+
matrix:
44+
os: [ macos-latest ]
45+
arkd-version: [ 'v0.5.9' ]
46+
runs-on: ${{ matrix.os }}
47+
steps:
48+
- uses: actions/checkout@v4
49+
- uses: extractions/setup-just@v2
50+
- uses: dtolnay/rust-toolchain@master
51+
with:
52+
toolchain: stable
53+
targets: wasm32-unknown-unknown
54+
- name: Install wasm-pack
55+
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
56+
57+
- name: Install LLVM and Clang
58+
if: contains(matrix.os, 'macos')
59+
run: brew install llvm
60+
- uses: Swatinem/rust-cache@v2
61+
62+
- name: Build crates for WASM
63+
run: PATH="/opt/homebrew/opt/llvm/bin:$PATH" cargo build -p ark-core -p ark-rest --target wasm32-unknown-unknown
64+

ark-rest/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@ uuid = { version = "^1.8", default-features = false, features = ["serde", "v4",
1919
getrandom = { version = "0.2", features = ["js"] }
2020

2121
[dev-dependencies]
22-
tokio = { version = "1.41.0", features = ["macros", "rt"] }
22+
js-sys = "0.3"
23+
tokio = { version = "1.42", features = ["macros", "rt"] }
24+
wasm-bindgen = "0.2"
25+
wasm-bindgen-futures = "0.4"
26+
wasm-bindgen-test = "0.3"
27+
web-sys = { version = "0.3.77", features = ["console"] }

ark-rest/src/client.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,14 @@ impl Client {
2929
}
3030

3131
pub async fn get_info(&self) -> Result<ark_core::server::Info, Error> {
32-
let _info = ark_service_get_info(&self.configuration).await?;
32+
let info = ark_service_get_info(&self.configuration).await?;
3333

34-
// TODO: Mapping from the `ark-rest` generated types to the `ark_core` types is too much of
34+
// TODO: Mapping from the `ark-rest` generated types to the `ark_core` types is alot of
3535
// a pain given that every field is currently optional. We are waiting for an update to the
36-
// swagger file to continue.
36+
// swagger file to make this nicer
3737

38-
//let info = info.try_into()?;
38+
let info = info.try_into().map_err(|e| Error(Box::new(e)))?;
3939

40-
// Ok(info)
41-
42-
todo!()
40+
Ok(info)
4341
}
4442
}

ark-rest/src/models/v1_get_info_response.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,18 @@
99
*/
1010

1111
use crate::models;
12+
use bitcoin::address::NetworkUnchecked;
13+
use bitcoin::secp256k1;
14+
use bitcoin::Address;
15+
use bitcoin::Amount;
16+
use bitcoin::Denomination;
17+
use bitcoin::Network;
18+
use bitcoin::PublicKey;
1219
use serde::Deserialize;
1320
use serde::Serialize;
21+
use std::error::Error as StdError;
22+
use std::fmt;
23+
use std::str::FromStr;
1424

1525
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1626
pub struct V1GetInfoResponse {
@@ -61,3 +71,111 @@ impl V1GetInfoResponse {
6171
}
6272
}
6373
}
74+
75+
#[derive(Debug)]
76+
pub enum ConversionError {
77+
MissingPubkey,
78+
MissingRoundLifetime,
79+
MissingUnilateralExitDelay,
80+
MissingRoundInterval,
81+
MissingNetwork,
82+
MissingDust,
83+
MissingBoardingDescriptorTemplate,
84+
MissingVtxoDescriptorTemplate,
85+
MissingForfeitAddress,
86+
InvalidNetwork,
87+
ParseError(&'static str),
88+
}
89+
90+
impl fmt::Display for ConversionError {
91+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92+
match self {
93+
ConversionError::MissingPubkey => write!(f, "Missing pubkey field"),
94+
ConversionError::MissingRoundLifetime => write!(f, "Missing round lifetime field"),
95+
ConversionError::MissingUnilateralExitDelay => {
96+
write!(f, "Missing unilateral exit delay field")
97+
}
98+
ConversionError::MissingRoundInterval => write!(f, "Missing round interval field"),
99+
ConversionError::MissingNetwork => write!(f, "Missing network field"),
100+
ConversionError::MissingDust => write!(f, "Missing dust field"),
101+
ConversionError::MissingBoardingDescriptorTemplate => {
102+
write!(f, "Missing boarding descriptor template field")
103+
}
104+
ConversionError::InvalidNetwork => write!(f, "Invalid network value"),
105+
ConversionError::ParseError(msg) => write!(f, "Parse error: {}", msg),
106+
ConversionError::MissingVtxoDescriptorTemplate => {
107+
write!(f, "Missing vtxo descriptor template",)
108+
}
109+
ConversionError::MissingForfeitAddress => write!(f, "Missing forfeit address"),
110+
}
111+
}
112+
}
113+
114+
impl StdError for ConversionError {}
115+
116+
impl TryFrom<V1GetInfoResponse> for ark_core::server::Info {
117+
type Error = ConversionError;
118+
119+
fn try_from(value: V1GetInfoResponse) -> Result<Self, Self::Error> {
120+
let pubkey = value.pubkey.ok_or(ConversionError::MissingPubkey)?;
121+
let pubkey = secp256k1::PublicKey::from_str(pubkey.as_str())
122+
.map_err(|_| ConversionError::ParseError("Failed parsing pubkey"))?;
123+
124+
let round_interval = value
125+
.round_interval
126+
.ok_or(ConversionError::MissingRoundInterval)?
127+
.parse::<i64>()
128+
.map_err(|_| ConversionError::ParseError("Failed to parse round_interval"))?;
129+
130+
let network = value.network.ok_or(ConversionError::MissingNetwork)?;
131+
let network =
132+
Network::from_str(network.as_str()).map_err(|_| ConversionError::InvalidNetwork)?;
133+
134+
let dust = value.dust.ok_or(ConversionError::MissingDust)?;
135+
let dust = Amount::from_str_in(dust.as_str(), Denomination::Satoshi)
136+
.map_err(|_| ConversionError::ParseError("Failed to parse dust"))?;
137+
138+
let unilateral_exit_delay = value
139+
.unilateral_exit_delay
140+
.ok_or(ConversionError::MissingUnilateralExitDelay)?
141+
.parse()
142+
.map_err(|_| ConversionError::ParseError("Failed to parse unilateral_exit_delay"))?;
143+
144+
let vtxo_tree_expiry = value
145+
.round_lifetime
146+
// TODO: our ark server doesn't return this value. I'm not sure it is mandatory?
147+
.unwrap_or("30".to_string());
148+
149+
let vtxo_tree_expiry_seconds = u32::from_str(vtxo_tree_expiry.as_str()).map_err(|_| {
150+
ConversionError::ParseError("Failed to parse round_lifetime to seconds")
151+
})?;
152+
let vtxo_tree_expiry = bitcoin::Sequence::from_seconds_ceil(vtxo_tree_expiry_seconds)
153+
.map_err(|_| ConversionError::ParseError("Failed to parse round_lifetime"))?;
154+
155+
let forfeit_address = value
156+
.forfeit_address
157+
.ok_or(ConversionError::MissingForfeitAddress)?;
158+
let forfeit_address: Address<NetworkUnchecked> = forfeit_address
159+
.parse()
160+
.map_err(|_| ConversionError::ParseError("Failed parsing forfeit address"))?;
161+
162+
let vtxo_descriptor_templates = value
163+
.vtxo_descriptor_templates
164+
.ok_or(ConversionError::MissingVtxoDescriptorTemplate)?;
165+
let boarding_descriptor_template = value
166+
.boarding_descriptor_template
167+
.ok_or(ConversionError::MissingBoardingDescriptorTemplate)?;
168+
169+
Ok(ark_core::server::Info {
170+
pk: pubkey,
171+
vtxo_tree_expiry,
172+
unilateral_exit_delay,
173+
round_interval,
174+
network,
175+
dust,
176+
boarding_descriptor_template,
177+
vtxo_descriptor_templates,
178+
forfeit_address: forfeit_address.assume_checked(),
179+
})
180+
}
181+
}

ark-rest/tests/wasm.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#[ignore] // we want to run this test only manually as it needs a full ark server
2+
#[wasm_bindgen_test::wasm_bindgen_test]
3+
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
4+
async fn test_get_info() {
5+
use ark_rest::Client;
6+
7+
let server_url = "http://localhost:7070".to_string();
8+
9+
let client = Client::new(server_url);
10+
11+
match client.get_info().await {
12+
Ok(info) => {
13+
assert!(info.round_interval > 0, "Round interval should be positive");
14+
15+
web_sys::console::log_1(&format!("Got info: {:?}", info).into());
16+
}
17+
Err(err) => {
18+
web_sys::console::error_1(&format!("Error getting info: {:?}", err).into());
19+
panic!("get_info failed with error: {:?}", err);
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)