Compare commits

...

8 Commits

Author SHA1 Message Date
5chdn
04dad4a140 ethcore-sync: accept service transactions from parity and parity-ethereum 2018-07-05 22:41:21 +02:00
5chdn
338e56907f rpc: fix parity_net_peers test 2018-07-05 22:40:09 +02:00
5chdn
40b55f6ce5 Merge branch 'master' into a5-parity-ethereum 2018-07-05 19:18:17 +02:00
5chdn
207901c7f5 rpc: fix sync provider in tests 2018-07-05 19:17:17 +02:00
5chdn
f428e2a60c parity: fix --identity tests 2018-07-05 19:16:38 +02:00
5chdn
64957e05aa ethcore: only accept service transactions from parity-ethereum nodes 2018-07-05 19:16:01 +02:00
5chdn
8a698d6fb0 parity-ethereum: rename crate 🌵 2018-07-05 15:27:34 +02:00
5chdn
f2a23fcb22 parity-version: major bump to 2.0.0 🎉 2018-07-05 13:56:16 +02:00
11 changed files with 106 additions and 98 deletions

126
Cargo.lock generated
View File

@@ -1969,7 +1969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2063,8 +2063,64 @@ dependencies = [
]
[[package]]
name = "parity"
name = "parity-clib"
version = "1.12.0"
dependencies = [
"parity-ethereum 2.0.0",
]
[[package]]
name = "parity-dapps"
version = "1.12.0"
dependencies = [
"base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bytes 0.1.0",
"ethcore-devtools 1.12.0",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fetch 0.1.0",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)",
"jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)",
"keccak-hash 0.1.2",
"linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
"node-health 0.1.0",
"parity-dapps-glue 1.9.1",
"parity-hash-fetch 1.12.0",
"parity-reactor 0.1.0",
"parity-version 2.0.0",
"parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"registrar 0.0.1",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-dapps-glue"
version = "1.9.1"
dependencies = [
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-ethereum"
version = "2.0.0"
dependencies = [
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2115,7 +2171,7 @@ dependencies = [
"parity-rpc 1.12.0",
"parity-rpc-client 1.4.0",
"parity-updater 1.12.0",
"parity-version 1.12.0",
"parity-version 2.0.0",
"parity-whisper 0.1.0",
"parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"path 0.1.0",
@@ -2138,62 +2194,6 @@ dependencies = [
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-clib"
version = "1.12.0"
dependencies = [
"parity 1.12.0",
]
[[package]]
name = "parity-dapps"
version = "1.12.0"
dependencies = [
"base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bytes 0.1.0",
"ethcore-devtools 1.12.0",
"ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fetch 0.1.0",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)",
"jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.11)",
"keccak-hash 0.1.2",
"linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
"node-health 0.1.0",
"parity-dapps-glue 1.9.1",
"parity-hash-fetch 1.12.0",
"parity-reactor 0.1.0",
"parity-version 1.12.0",
"parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"registrar 0.0.1",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-dapps-glue"
version = "1.9.1"
dependencies = [
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-hash-fetch"
version = "1.12.0"
@@ -2311,7 +2311,7 @@ dependencies = [
"order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0",
"parity-updater 1.12.0",
"parity-version 1.12.0",
"parity-version 2.0.0",
"parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"patricia-trie 0.2.0",
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2381,7 +2381,7 @@ dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-hash-fetch 1.12.0",
"parity-version 1.12.0",
"parity-version 2.0.0",
"parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"path 0.1.0",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2392,7 +2392,7 @@ dependencies = [
[[package]]
name = "parity-version"
version = "1.12.0"
version = "2.0.0"
dependencies = [
"ethcore-bytes 0.1.0",
"rlp 0.2.1",
@@ -2762,7 +2762,7 @@ dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]

View File

@@ -1,8 +1,8 @@
[package]
description = "Parity Ethereum client"
name = "parity"
name = "parity-ethereum"
# NOTE Make sure to update util/version/Cargo.toml as well
version = "1.12.0"
version = "2.0.0"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]

View File

@@ -48,16 +48,24 @@ fn accepts_service_transaction(client_id: &str) -> bool {
// Parity versions starting from this will accept service-transactions
const SERVICE_TRANSACTIONS_VERSION: (u32, u32) = (1u32, 6u32);
// Parity client string prefix
const PARITY_CLIENT_ID_PREFIX: &'static str = "Parity/v";
const LEGACY_CLIENT_ID_PREFIX: &'static str = "Parity/v";
const PARITY_CLIENT_ID_PREFIX: &'static str = "Parity-Ethereum/v";
if !client_id.starts_with(PARITY_CLIENT_ID_PREFIX) {
return false;
}
if client_id.starts_with(LEGACY_CLIENT_ID_PREFIX) {
let ver: Vec<u32> = client_id[LEGACY_CLIENT_ID_PREFIX.len()..].split('.')
.take(2)
.filter_map(|s| s.parse().ok())
.collect();
ver.len() == 2 && (ver[0] > SERVICE_TRANSACTIONS_VERSION.0 || (ver[0] == SERVICE_TRANSACTIONS_VERSION.0 && ver[1] >= SERVICE_TRANSACTIONS_VERSION.1))
} else if client_id.starts_with(PARITY_CLIENT_ID_PREFIX) {
let ver: Vec<u32> = client_id[PARITY_CLIENT_ID_PREFIX.len()..].split('.')
.take(2)
.filter_map(|s| s.parse().ok())
.collect();
ver.len() == 2 && (ver[0] > SERVICE_TRANSACTIONS_VERSION.0 || (ver[0] == SERVICE_TRANSACTIONS_VERSION.0 && ver[1] >= SERVICE_TRANSACTIONS_VERSION.1))
ver.len() == 2 && (ver[0] > SERVICE_TRANSACTIONS_VERSION.0 || (ver[0] == SERVICE_TRANSACTIONS_VERSION.0 && ver[1] >= SERVICE_TRANSACTIONS_VERSION.1))
} else {
return false;
}
}
/// The Chain Sync Propagator: propagates data to peers
@@ -577,13 +585,13 @@ mod tests {
io.peers_info.insert(1, "Geth".to_owned());
// and peer#2 is Parity, accepting service transactions
insert_dummy_peer(&mut sync, 2, block_hash);
io.peers_info.insert(2, "Parity/v1.6".to_owned());
io.peers_info.insert(2, "Parity-Ethereum/v2.6".to_owned());
// and peer#3 is Parity, discarding service transactions
insert_dummy_peer(&mut sync, 3, block_hash);
io.peers_info.insert(3, "Parity/v1.5".to_owned());
// and peer#4 is Parity, accepting service transactions
insert_dummy_peer(&mut sync, 4, block_hash);
io.peers_info.insert(4, "Parity/v1.7.3-ABCDEFGH".to_owned());
io.peers_info.insert(4, "Parity-Ethereum/v2.7.3-ABCDEFGH".to_owned());
// and new service transaction is propagated to peers
SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
@@ -607,7 +615,7 @@ mod tests {
// when peer#1 is Parity, accepting service transactions
insert_dummy_peer(&mut sync, 1, block_hash);
io.peers_info.insert(1, "Parity/v1.6".to_owned());
io.peers_info.insert(1, "Parity-Ethereum/v2.6".to_owned());
// and service + non-service transactions are propagated to peers
SyncPropagator::propagate_new_transactions(&mut sync, &mut io);

View File

@@ -10,8 +10,8 @@ name = "parity"
crate-type = ["cdylib", "staticlib"]
[dependencies]
parity = { path = "../", default-features = false }
parity-ethereum = { path = "../", default-features = false }
[features]
default = []
final = ["parity/final"]
final = ["parity-ethereum/final"]

View File

@@ -17,7 +17,7 @@
//! Note that all the structs and functions here are documented in `parity.h`, to avoid
//! duplicating documentation.
extern crate parity;
extern crate parity_ethereum;
use std::os::raw::{c_char, c_void, c_int};
use std::panic;
@@ -56,7 +56,7 @@ pub extern fn parity_config_from_cli(args: *const *const c_char, args_lens: *con
args
};
match parity::Configuration::parse_cli(&args) {
match parity_ethereum::Configuration::parse_cli(&args) {
Ok(mut cfg) => {
// Always disable the auto-updater when used as a library.
cfg.args.arg_auto_update = "none".to_owned();
@@ -77,7 +77,7 @@ pub extern fn parity_config_from_cli(args: *const *const c_char, args_lens: *con
pub extern fn parity_config_destroy(cfg: *mut c_void) {
unsafe {
let _ = panic::catch_unwind(|| {
let _cfg = Box::from_raw(cfg as *mut parity::Configuration);
let _cfg = Box::from_raw(cfg as *mut parity_ethereum::Configuration);
});
}
}
@@ -89,7 +89,7 @@ pub extern fn parity_start(cfg: *const ParityParams, output: *mut *mut c_void) -
*output = ptr::null_mut();
let cfg: &ParityParams = &*cfg;
let config = Box::from_raw(cfg.configuration as *mut parity::Configuration);
let config = Box::from_raw(cfg.configuration as *mut parity_ethereum::Configuration);
let on_client_restart_cb = {
struct Cb(Option<extern "C" fn(*mut c_void, *const c_char, usize)>, *mut c_void);
@@ -106,16 +106,16 @@ pub extern fn parity_start(cfg: *const ParityParams, output: *mut *mut c_void) -
move |new_chain: String| { cb.call(new_chain); }
};
let action = match parity::start(*config, on_client_restart_cb, || {}) {
let action = match parity_ethereum::start(*config, on_client_restart_cb, || {}) {
Ok(action) => action,
Err(_) => return 1,
};
match action {
parity::ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 },
parity::ExecutionAction::Instant(None) => 0,
parity::ExecutionAction::Running(client) => {
*output = Box::into_raw(Box::<parity::RunningClient>::new(client)) as *mut c_void;
parity_ethereum::ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 },
parity_ethereum::ExecutionAction::Instant(None) => 0,
parity_ethereum::ExecutionAction::Running(client) => {
*output = Box::into_raw(Box::<parity_ethereum::RunningClient>::new(client)) as *mut c_void;
0
}
}
@@ -127,7 +127,7 @@ pub extern fn parity_start(cfg: *const ParityParams, output: *mut *mut c_void) -
pub extern fn parity_destroy(client: *mut c_void) {
unsafe {
let _ = panic::catch_unwind(|| {
let client = Box::from_raw(client as *mut parity::RunningClient);
let client = Box::from_raw(client as *mut parity_ethereum::RunningClient);
client.shutdown();
});
}
@@ -137,7 +137,7 @@ pub extern fn parity_destroy(client: *mut c_void) {
pub extern fn parity_rpc(client: *mut c_void, query: *const char, len: usize, out_str: *mut c_char, out_len: *mut usize) -> c_int {
unsafe {
panic::catch_unwind(|| {
let client: &mut parity::RunningClient = &mut *(client as *mut parity::RunningClient);
let client: &mut parity_ethereum::RunningClient = &mut *(client as *mut parity_ethereum::RunningClient);
let query_str = {
let string = slice::from_raw_parts(query as *const u8, len);

View File

@@ -103,7 +103,7 @@ impl Configuration {
/// # Example
///
/// ```
/// let _cfg = parity::Configuration::parse_cli(&["--light", "--chain", "koven"]).unwrap();
/// let _cfg = parity_ethereum::Configuration::parse_cli(&["--light", "--chain", "koven"]).unwrap();
/// ```
pub fn parse_cli<S: AsRef<str>>(command: &[S]) -> Result<Self, ArgsError> {
let config = Configuration {
@@ -759,7 +759,7 @@ impl Configuration {
ret.client_version = {
let mut client_version = version();
if !self.args.arg_identity.is_empty() {
// Insert name after the "Parity/" at the beginning of version string.
// Insert name after the "Parity-Ethereum/" at the beginning of version string.
let idx = client_version.find('/').unwrap_or(client_version.len());
client_version.insert_str(idx, &format!("/{}", self.args.arg_identity));
}
@@ -1809,7 +1809,7 @@ mod tests {
match conf.into_command().unwrap().cmd {
Cmd::Run(c) => {
assert_eq!(c.name, "Somebody");
assert!(c.net_conf.client_version.starts_with("Parity/Somebody/"));
assert!(c.net_conf.client_version.starts_with("Parity-Ethereum/Somebody/"));
}
_ => panic!("Should be Cmd::Run"),
}

View File

@@ -18,7 +18,7 @@
#![warn(missing_docs)]
extern crate parity;
extern crate parity_ethereum;
extern crate ctrlc;
extern crate dir;
@@ -39,7 +39,7 @@ use std::sync::Arc;
use ctrlc::CtrlC;
use dir::default_hypervisor_path;
use fdlimit::raise_fd_limit;
use parity::{start, ExecutionAction};
use parity_ethereum::{start, ExecutionAction};
use parking_lot::{Condvar, Mutex};
fn updates_path(name: &str) -> PathBuf {
@@ -133,7 +133,7 @@ fn main_direct(force_can_restart: bool) -> i32 {
let mut conf = {
let args = std::env::args().collect::<Vec<_>>();
parity::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit())
parity_ethereum::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit())
};
if let Some(spec_override) = take_spec_name_override() {

View File

@@ -75,7 +75,7 @@ impl SyncProvider for TestSyncProvider {
vec![
PeerInfo {
id: Some("node1".to_owned()),
client_version: "Parity/1".to_owned(),
client_version: "Parity-Ethereum/1".to_owned(),
capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()],
remote_address: "127.0.0.1:7777".to_owned(),
local_address: "127.0.0.1:8888".to_owned(),
@@ -88,7 +88,7 @@ impl SyncProvider for TestSyncProvider {
},
PeerInfo {
id: None,
client_version: "Parity/2".to_owned(),
client_version: "Parity-Ethereum/2".to_owned(),
capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()],
remote_address: "Handshake".to_owned(),
local_address: "127.0.0.1:3333".to_owned(),

View File

@@ -329,7 +329,7 @@ fn rpc_parity_net_peers() {
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_netPeers", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/62","eth/63"],"id":"node1","name":"Parity/1","network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":62},"pip":null}},{"caps":["eth/63","eth/64"],"id":null,"name":"Parity/2","network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":64},"pip":null}}]},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/62","eth/63"],"id":"node1","name":"Parity-Ethereum/1","network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":62},"pip":null}},{"caps":["eth/63","eth/64"],"id":null,"name":"Parity-Ethereum/2","network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":64},"pip":null}}]},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}

View File

@@ -3,7 +3,7 @@
[package]
name = "parity-version"
# NOTE: this value is used for Parity version string (via env CARGO_PKG_VERSION)
version = "1.12.0"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"

View File

@@ -54,7 +54,7 @@ pub fn version() -> String {
let sha3_dash = if sha3.is_empty() { "" } else { "-" };
let commit_date = vergen::commit_date().replace("-", "");
let date_dash = if commit_date.is_empty() { "" } else { "-" };
format!("Parity/v{}-{}{}{}{}{}/{}/rustc{}", env!("CARGO_PKG_VERSION"), THIS_TRACK, sha3_dash, sha3, date_dash, commit_date, platform(), generated::rustc_version())
format!("Parity-Ethereum/v{}-{}{}{}{}{}/{}/rustc{}", env!("CARGO_PKG_VERSION"), THIS_TRACK, sha3_dash, sha3, date_dash, commit_date, platform(), generated::rustc_version())
}
/// Get the standard version data for this software.
@@ -65,7 +65,7 @@ pub fn version_data() -> Bytes {
(env!("CARGO_PKG_VERSION_MINOR").parse::<u32>().expect("Environment variables are known to be valid; qed") << 8) +
env!("CARGO_PKG_VERSION_PATCH").parse::<u32>().expect("Environment variables are known to be valid; qed");
s.append(&v);
s.append(&"Parity");
s.append(&"Parity-Ethereum");
s.append(&generated::rustc_version());
s.append(&&Target::os()[0..2]);
s.out()