Skip to content

Commit 5df2c6f

Browse files
authoredSep 10, 2024··
Setup initial integration tests & misc configurations (#9)
1 parent 67af498 commit 5df2c6f

12 files changed

+894
-68
lines changed
 

‎.editorconfig

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true

‎.github/workflows/ci.yml

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
run-tests:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v3
18+
19+
- name: Set up Rust
20+
uses: actions-rs/toolchain@v1
21+
with:
22+
toolchain: stable
23+
profile: minimal
24+
override: true
25+
26+
- name: Cache cargo registry
27+
uses: actions/cache@v3
28+
with:
29+
path: ~/.cargo/registry
30+
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
31+
restore-keys: |
32+
${{ runner.os }}-cargo-registry-
33+
34+
- name: Run tests
35+
run: cargo test

‎.vscode/launch.json

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"type": "lldb",
9+
"request": "launch",
10+
"name": "Debug unit tests in library 'spaced'",
11+
"cargo": {
12+
"args": [
13+
"test",
14+
"--no-run",
15+
"--lib",
16+
"--package=spaced"
17+
],
18+
"filter": {
19+
"name": "spaced",
20+
"kind": "lib"
21+
}
22+
},
23+
"args": [],
24+
"cwd": "${workspaceFolder}"
25+
},
26+
{
27+
"type": "lldb",
28+
"request": "launch",
29+
"name": "Debug executable 'space-cli'",
30+
"cargo": {
31+
"args": [
32+
"build",
33+
"--bin=space-cli",
34+
"--package=spaced"
35+
],
36+
"filter": {
37+
"name": "space-cli",
38+
"kind": "bin"
39+
}
40+
},
41+
"args": [],
42+
"cwd": "${workspaceFolder}"
43+
},
44+
{
45+
"type": "lldb",
46+
"request": "launch",
47+
"name": "Debug unit tests in executable 'space-cli'",
48+
"cargo": {
49+
"args": [
50+
"test",
51+
"--no-run",
52+
"--bin=space-cli",
53+
"--package=spaced"
54+
],
55+
"filter": {
56+
"name": "space-cli",
57+
"kind": "bin"
58+
}
59+
},
60+
"args": [],
61+
"cwd": "${workspaceFolder}"
62+
},
63+
{
64+
"type": "lldb",
65+
"request": "launch",
66+
"name": "Debug executable 'spaced'",
67+
"cargo": {
68+
"args": [
69+
"build",
70+
"--bin=spaced",
71+
"--package=spaced"
72+
],
73+
"filter": {
74+
"name": "spaced",
75+
"kind": "bin"
76+
}
77+
},
78+
"args": [],
79+
"cwd": "${workspaceFolder}"
80+
},
81+
{
82+
"type": "lldb",
83+
"request": "launch",
84+
"name": "Debug unit tests in executable 'spaced'",
85+
"cargo": {
86+
"args": [
87+
"test",
88+
"--no-run",
89+
"--bin=spaced",
90+
"--package=spaced"
91+
],
92+
"filter": {
93+
"name": "spaced",
94+
"kind": "bin"
95+
}
96+
},
97+
"args": [],
98+
"cwd": "${workspaceFolder}"
99+
},
100+
{
101+
"type": "lldb",
102+
"request": "launch",
103+
"name": "Debug unit tests in library 'protocol'",
104+
"cargo": {
105+
"args": [
106+
"test",
107+
"--no-run",
108+
"--lib",
109+
"--package=protocol"
110+
],
111+
"filter": {
112+
"name": "protocol",
113+
"kind": "lib"
114+
}
115+
},
116+
"args": [],
117+
"cwd": "${workspaceFolder}"
118+
},
119+
{
120+
"type": "lldb",
121+
"request": "launch",
122+
"name": "Debug unit tests in library 'wallet'",
123+
"cargo": {
124+
"args": [
125+
"test",
126+
"--no-run",
127+
"--lib",
128+
"--package=wallet"
129+
],
130+
"filter": {
131+
"name": "wallet",
132+
"kind": "lib"
133+
}
134+
},
135+
"args": [],
136+
"cwd": "${workspaceFolder}"
137+
}
138+
]
139+
}

‎.vscode/settings.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"cSpell.words": [
3+
"bitcoind",
4+
"regtest",
5+
"tempdir",
6+
"tempfile"
7+
]
8+
}

‎Cargo.lock

+518-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎README.md

+7-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
# Spaced - Bitcoin Spaces daemon
22

3-
Spaces is a naming protocol that leverages the existing infrastructure and security of Bitcoin without requiring a new blockchain or any modifications to Bitcoin itself [learn more](https://spacesprotocol.org).
3+
Spaces is a naming protocol that leverages the existing infrastructure and security of Bitcoin without requiring a new blockchain or any modifications to Bitcoin itself [learn more](https://spacesprotocol.org).
44

55
## Project Structure
66

7-
| Package | Requires std | |
8-
|----------|------------------|-------------------------------------------------|
9-
| node | Yes | Daemon and wallet service | |
10-
| wallet | Yes (no-std WIP) | wallet library for building spaces transactions |
11-
| protocol | No | Protocol consensus library |
12-
7+
| Package | Requires std | Description |
8+
|----------|------------------|------------------------------------------------|
9+
| node | Yes | Daemon and wallet service |
10+
| wallet | Yes (no-std WIP) | wallet library for building spaces transactions|
11+
| protocol | No | Protocol consensus library |
1312

1413
## Setup
1514

16-
First, download Bitcoin Core and set it up to connect to `regtest`
15+
First, download Bitcoin Core and set it up to connect to `regtest`
1716
using these steps:
1817

1918
```bash
@@ -34,5 +33,3 @@ Connect `spaced` to Bitcoin core
3433
```bash
3534
spaced --chain regtest --bitcoin-rpc-user test --bitcoin-rpc-password test
3635
```
37-
38-

‎node/Cargo.toml

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,9 @@ spacedb = { git = "https://github.com/spacesprotocol/spacedb", tag = "0.0.2" }
3535
base64 = "0.22.1"
3636
futures = "0.3.30"
3737
reqwest = { version = "0.12.5", features = ["json", "blocking"] }
38-
threadpool = "1.8.1"
38+
threadpool = "1.8.1"
39+
40+
[dev-dependencies]
41+
assert_cmd = "2.0.16"
42+
bitcoind = { version = "0.36.0", features = ["26_0"] }
43+
predicates = "3.1.2"

‎node/src/rpc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub(crate) type Responder<T> = oneshot::Sender<T>;
5151

5252
#[derive(Debug, Clone, Serialize, Deserialize)]
5353
pub struct ServerInfo {
54-
chain: ExtendedNetwork,
54+
pub chain: ExtendedNetwork,
5555
tip: ChainAnchor,
5656
}
5757

‎node/src/source.rs

-49
Original file line numberDiff line numberDiff line change
@@ -732,52 +732,3 @@ impl BlockSource for BitcoinBlockSource {
732732
.send_json_blocking(&self.client, &self.rpc.get_block_count())?)
733733
}
734734
}
735-
736-
#[cfg(test)]
737-
mod test {
738-
use protocol::{bitcoin::BlockHash, constants::ChainAnchor};
739-
740-
use crate::source::{BitcoinRpc, BitcoinRpcAuth, BlockEvent, BlockFetcher};
741-
742-
#[test]
743-
fn test_fetcher() -> anyhow::Result<()> {
744-
let rpc = BitcoinRpc::new(
745-
"http://127.0.0.1:18332",
746-
BitcoinRpcAuth::UserPass("test".to_string(), "test".to_string()),
747-
);
748-
749-
let client = reqwest::blocking::Client::new();
750-
let count: u32 = rpc.send_json_blocking(&client, &rpc.get_block_count())?;
751-
752-
let checkpoint = count - 10;
753-
let start_block_hash: BlockHash =
754-
rpc.send_json_blocking(&client, &rpc.get_block_hash(checkpoint))?;
755-
756-
let (fetcher, receiver) = BlockFetcher::new(rpc, client, 8);
757-
758-
println!("fetcher checkpoint block {}", checkpoint);
759-
760-
fetcher.start(ChainAnchor {
761-
hash: start_block_hash,
762-
height: checkpoint,
763-
});
764-
765-
println!("fetcher receiving blocks");
766-
while let Ok(event) = receiver.recv() {
767-
match event {
768-
BlockEvent::Block(id, _) => {
769-
println!("got block {}", id.height);
770-
if id.height >= count {
771-
break;
772-
}
773-
}
774-
BlockEvent::Error(e) => {
775-
println!("error: {}", e.to_string());
776-
break;
777-
}
778-
}
779-
}
780-
781-
Ok(())
782-
}
783-
}

‎node/tests/fetcher_tests.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
pub mod utils;
2+
3+
#[cfg(test)]
4+
mod tests {
5+
use std::{
6+
sync::mpsc::TryRecvError,
7+
time::{Duration, Instant},
8+
};
9+
10+
use crate::utils::SpaceD;
11+
use anyhow::Result;
12+
use bitcoind::bitcoincore_rpc::RpcApi;
13+
use protocol::constants::ChainAnchor;
14+
use reqwest::blocking::Client;
15+
use spaced::source::{BitcoinRpc, BitcoinRpcAuth, BlockEvent, BlockFetcher};
16+
use wallet::bitcoin::Network;
17+
18+
#[test]
19+
fn test_block_fetching_from_bitcoin_rpc() -> Result<()> {
20+
let spaced = SpaceD::new()?;
21+
let fetcher_rpc = BitcoinRpc::new(
22+
&spaced.bitcoind.rpc_url(),
23+
BitcoinRpcAuth::UserPass("user".to_string(), "password".to_string()),
24+
);
25+
let miner_addr = spaced
26+
.bitcoind
27+
.client
28+
.get_new_address(None, None)?
29+
.require_network(Network::Regtest)?;
30+
const GENERATED_BLOCKS: u32 = 10;
31+
spaced
32+
.bitcoind
33+
.client
34+
.generate_to_address(GENERATED_BLOCKS as u64, &miner_addr)?;
35+
36+
let client = Client::new();
37+
let (fetcher, receiver) = BlockFetcher::new(fetcher_rpc.clone(), client.clone(), 8);
38+
fetcher.start(ChainAnchor {
39+
hash: fetcher_rpc.send_json_blocking(&client, &fetcher_rpc.get_block_hash(0))?,
40+
height: 0,
41+
});
42+
43+
let mut start_block = 0;
44+
let timeout = Duration::from_secs(5);
45+
let start_time = Instant::now();
46+
47+
loop {
48+
if start_time.elapsed() > timeout {
49+
panic!("Test timed out after {:?}", timeout);
50+
}
51+
match receiver.try_recv() {
52+
Ok(BlockEvent::Block(id, _)) => {
53+
start_block += 1;
54+
if id.height == GENERATED_BLOCKS {
55+
break;
56+
}
57+
}
58+
Ok(BlockEvent::Error(e)) => panic!("Unexpected error: {}", e),
59+
Err(TryRecvError::Empty) => {
60+
std::thread::sleep(Duration::from_millis(10));
61+
}
62+
Err(TryRecvError::Disconnected) => panic!("Disconnected unexpectedly"),
63+
}
64+
}
65+
assert_eq!(
66+
start_block, GENERATED_BLOCKS,
67+
"Not all blocks were received"
68+
);
69+
Ok(())
70+
}
71+
}

‎node/tests/space_cli_tests.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
pub mod utils;
2+
3+
#[cfg(test)]
4+
mod tests {
5+
use crate::utils::SpaceD;
6+
use anyhow::Result;
7+
use assert_cmd::prelude::*;
8+
use predicates::prelude::*;
9+
use serde_json::from_str;
10+
use spaced::config::ExtendedNetwork;
11+
use spaced::rpc::ServerInfo;
12+
use std::process::Command;
13+
14+
#[tokio::test]
15+
async fn test_get_server_info() -> Result<()> {
16+
env_logger::init();
17+
let spaced = SpaceD::new()?;
18+
19+
Command::cargo_bin("space-cli")?
20+
.arg("--chain")
21+
.arg("regtest")
22+
.arg("--spaced-rpc-url")
23+
.arg(spaced.spaced_rpc_url())
24+
.arg("getserverinfo")
25+
.assert()
26+
.success()
27+
.stdout(predicate::function(|x: &str| {
28+
let info: ServerInfo = from_str(x).unwrap();
29+
return info.chain == ExtendedNetwork::Regtest;
30+
}));
31+
32+
Ok(())
33+
}
34+
}

‎node/tests/utils.rs

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use anyhow::{anyhow, Result};
2+
use assert_cmd::cargo::CommandCargoExt;
3+
use bitcoind::{get_available_port, tempfile::tempdir, BitcoinD, Conf};
4+
use log::{debug, error};
5+
use std::{
6+
net::Ipv4Addr,
7+
process::{Child, Command},
8+
thread,
9+
time::Duration,
10+
};
11+
12+
#[derive(Debug)]
13+
pub struct SpaceD {
14+
pub bitcoind: BitcoinD,
15+
process: Child,
16+
rpc_port: u16,
17+
}
18+
19+
const LOCAL_IP: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
20+
21+
impl SpaceD {
22+
pub fn new() -> Result<Self> {
23+
let mut conf: Conf = Conf::default();
24+
// The RPC auth uses username "user" and password "password". If we
25+
// don't set this, bitcoind's RPC API becomes inaccessible to spaced due
26+
// to auth issues.
27+
conf.args = vec!["-regtest", "-fallbackfee=0.0001", "-rpcauth=user:70dbb4f60ccc95e154da97a43b7a9d06$00c10a3849edf2f10173e80d0bdadbde793ad9a80e6e6f9f71f978fb5c797343"];
28+
let bitcoind = BitcoinD::from_downloaded_with_conf(&conf).unwrap();
29+
debug!("bitcoind running on port {}", bitcoind.rpc_url());
30+
let rpc_port = get_available_port()?;
31+
let mut process = Command::cargo_bin("spaced")?
32+
.arg("--chain")
33+
.arg("regtest")
34+
.arg("--bitcoin-rpc-url")
35+
.arg(bitcoind.rpc_url())
36+
.arg("--bitcoin-rpc-user")
37+
.arg("user")
38+
.arg("--bitcoin-rpc-password")
39+
.arg("password")
40+
.arg("--block-index")
41+
.arg("--data-dir")
42+
.arg(tempdir()?.path())
43+
.arg("--rpc-port")
44+
.arg(rpc_port.to_string())
45+
.spawn()?;
46+
if let Some(_) = process.try_wait()? {
47+
error!("spaced failed to obtain port {}", rpc_port);
48+
return Err(anyhow!("port unavailable"));
49+
}
50+
thread::sleep(Duration::from_millis(100));
51+
assert!(process.stderr.is_none());
52+
debug!("spaced running on port {}", rpc_port);
53+
Ok(Self {
54+
bitcoind,
55+
process,
56+
rpc_port,
57+
})
58+
}
59+
60+
pub fn spaced_rpc_url(&self) -> String {
61+
format!("http://{}:{}", LOCAL_IP, self.rpc_port)
62+
}
63+
}
64+
65+
impl Drop for SpaceD {
66+
fn drop(&mut self) {
67+
debug!("killing spaced process");
68+
let _ = self.process.kill();
69+
}
70+
}

0 commit comments

Comments
 (0)
Please sign in to comment.