Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f838ddffe2 | |||
| 7f7c33d10b | |||
| e7fdd3d076 | |||
| 4dc89bd65f | |||
| 9aa3b9507d | |||
| 61e88f304b | |||
| 7ff043d8df | |||
| c904d245d2 |
@@ -43,3 +43,4 @@ Cargo.lock
|
||||
nym-connect/Cargo.lock
|
||||
.parcel-cache
|
||||
**/.DS_Store
|
||||
cpu-cycles/libcpucycles/build
|
||||
Generated
+4
-10
@@ -2706,9 +2706,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
version = "0.2.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@@ -3140,14 +3140,6 @@ dependencies = [
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-bandwidth-claim-contract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-bin-common"
|
||||
version = "0.2.0"
|
||||
@@ -3220,6 +3212,7 @@ dependencies = [
|
||||
"log",
|
||||
"nym-coconut-bandwidth-contract-common",
|
||||
"nym-coconut-dkg-common",
|
||||
"nym-contracts-common",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
"nym-network-defaults",
|
||||
@@ -3515,6 +3508,7 @@ dependencies = [
|
||||
"mixnode-common",
|
||||
"nym-bin-common",
|
||||
"nym-config",
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-nonexhaustive-delayqueue",
|
||||
"nym-pemstore",
|
||||
|
||||
@@ -22,7 +22,6 @@ members = [
|
||||
"clients/native",
|
||||
"clients/native/websocket-requests",
|
||||
"clients/socks5",
|
||||
"common/bandwidth-claim-contract",
|
||||
"common/bin-common",
|
||||
"common/client-libs/gateway-client",
|
||||
"common/client-libs/mixnet-client",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "nym-bandwidth-claim-contract"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// Serializable structures for what we find in common/crypto
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct PublicKey([u8; 32]);
|
||||
|
||||
impl PublicKey {
|
||||
pub fn new(bytes: [u8; 32]) -> Self {
|
||||
PublicKey(bytes)
|
||||
}
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for PublicKey {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct Signature([u8; 32], [u8; 32]);
|
||||
|
||||
impl Signature {
|
||||
pub fn new(bytes: [u8; 64]) -> Self {
|
||||
let mut sig1 = [0u8; 32];
|
||||
let mut sig2 = [0u8; 32];
|
||||
sig1.copy_from_slice(&bytes[..32]);
|
||||
sig2.copy_from_slice(&bytes[32..]);
|
||||
|
||||
Signature(sig1, sig2)
|
||||
}
|
||||
pub fn to_bytes(&self) -> [u8; 64] {
|
||||
let mut res = [0u8; 64];
|
||||
res[..32].copy_from_slice(&self.0);
|
||||
res[32..].copy_from_slice(&self.1);
|
||||
res
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
pub mod keys;
|
||||
pub mod msg;
|
||||
pub mod payment;
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::keys::PublicKey;
|
||||
use crate::payment::LinkPaymentData;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct InstantiateMsg {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
LinkPayment { data: LinkPaymentData },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
GetPayments {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<PublicKey>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::keys::{PublicKey, Signature};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct Payment {
|
||||
verification_key: PublicKey,
|
||||
gateway_identity: PublicKey,
|
||||
bandwidth: u64,
|
||||
}
|
||||
|
||||
impl Payment {
|
||||
pub fn new(verification_key: PublicKey, gateway_identity: PublicKey, bandwidth: u64) -> Self {
|
||||
Payment {
|
||||
verification_key,
|
||||
gateway_identity,
|
||||
bandwidth,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verification_key(&self) -> PublicKey {
|
||||
self.verification_key
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct LinkPaymentData {
|
||||
pub verification_key: PublicKey,
|
||||
pub gateway_identity: PublicKey,
|
||||
pub bandwidth: u64,
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl LinkPaymentData {
|
||||
pub fn new(
|
||||
verification_key: [u8; 32],
|
||||
gateway_identity: [u8; 32],
|
||||
bandwidth: u64,
|
||||
signature: [u8; 64],
|
||||
) -> Self {
|
||||
LinkPaymentData {
|
||||
verification_key: PublicKey::new(verification_key),
|
||||
gateway_identity: PublicKey::new(gateway_identity),
|
||||
bandwidth,
|
||||
signature: Signature::new(signature),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedPaymentResponse {
|
||||
pub payments: Vec<Payment>,
|
||||
pub per_page: usize,
|
||||
pub start_next_after: Option<PublicKey>,
|
||||
}
|
||||
|
||||
impl PagedPaymentResponse {
|
||||
pub fn new(
|
||||
payments: Vec<Payment>,
|
||||
per_page: usize,
|
||||
start_next_after: Option<PublicKey>,
|
||||
) -> Self {
|
||||
PagedPaymentResponse {
|
||||
payments,
|
||||
per_page,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ pub use cosmrs::tendermint::Time as TendermintTime;
|
||||
pub use cosmrs::tx::{self, Gas};
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
pub use cosmrs::{bip32, AccountId, Decimal, Denom};
|
||||
use cosmwasm_std::Addr;
|
||||
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
||||
pub use signing_client::Client as SigningNyxdClient;
|
||||
@@ -369,6 +370,15 @@ where
|
||||
&self.client_address.as_ref().unwrap()[0]
|
||||
}
|
||||
|
||||
pub fn cw_address(&self) -> Addr
|
||||
where
|
||||
C: SigningCosmWasmClient,
|
||||
{
|
||||
// the call to unchecked is fine here as we're converting directly from `AccountId`
|
||||
// which must have been a valid bech32 address
|
||||
Addr::unchecked(self.address().as_ref())
|
||||
}
|
||||
|
||||
pub fn signer(&self) -> &DirectSecp256k1HdWallet
|
||||
where
|
||||
C: SigningCosmWasmClient,
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::NyxdClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use nym_contracts_common::signing::Nonce;
|
||||
use nym_mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey};
|
||||
use nym_mixnet_contract_common::families::Family;
|
||||
use nym_mixnet_contract_common::mixnode::{
|
||||
@@ -390,6 +391,13 @@ pub trait MixnetQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_node_family_by_label(&self, label: &str) -> Result<Option<Family>, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel {
|
||||
label: label.to_string(),
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
|
||||
use nym_mixnet_contract_common::{
|
||||
@@ -290,7 +291,7 @@ pub trait MixnetSigningClient {
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
@@ -311,7 +312,7 @@ pub trait MixnetSigningClient {
|
||||
owner: AccountId,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
@@ -442,7 +443,7 @@ pub trait MixnetSigningClient {
|
||||
async fn bond_gateway(
|
||||
&self,
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
@@ -461,7 +462,7 @@ pub trait MixnetSigningClient {
|
||||
&self,
|
||||
owner: AccountId,
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::{Coin, Fee, NyxdClient};
|
||||
use async_trait::async_trait;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
|
||||
use nym_vesting_contract_common::messages::{
|
||||
@@ -43,7 +44,7 @@ pub trait VestingSigningClient {
|
||||
async fn vesting_bond_gateway(
|
||||
&self,
|
||||
gateway: Gateway,
|
||||
owner_signature: &str,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
@@ -61,7 +62,7 @@ pub trait VestingSigningClient {
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: &str,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
@@ -208,14 +209,14 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
|
||||
async fn vesting_bond_gateway(
|
||||
&self,
|
||||
gateway: Gateway,
|
||||
owner_signature: &str,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
||||
let req = VestingExecuteMsg::BondGateway {
|
||||
gateway,
|
||||
owner_signature: owner_signature.to_string(),
|
||||
owner_signature,
|
||||
amount: pledge.into(),
|
||||
};
|
||||
self.client
|
||||
@@ -272,7 +273,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: &str,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
@@ -281,7 +282,7 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
|
||||
VestingExecuteMsg::BondMixnode {
|
||||
mix_node,
|
||||
cost_params,
|
||||
owner_signature: owner_signature.to_string(),
|
||||
owner_signature,
|
||||
amount: pledge.into(),
|
||||
},
|
||||
vec![],
|
||||
|
||||
@@ -30,6 +30,7 @@ cosmwasm-std = { version = "1.0.0" }
|
||||
|
||||
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" }
|
||||
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
|
||||
nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmrs::AccountId;
|
||||
use cosmwasm_std::{Addr, Coin as CosmWasmCoin, Decimal};
|
||||
use log::error;
|
||||
use std::error::Error;
|
||||
use std::fmt::Display;
|
||||
|
||||
use cosmwasm_std::{Coin as CosmWasmCoin, Decimal};
|
||||
use log::error;
|
||||
use validator_client::nyxd::Coin;
|
||||
|
||||
// TODO: perhaps it should be moved to some global common crate?
|
||||
pub fn account_id_to_cw_addr(account_id: &AccountId) -> Addr {
|
||||
// the call to unchecked is fine here as we're converting directly from `AccountId`
|
||||
// which must have been a valid bech32 address
|
||||
Addr::unchecked(account_id.as_ref())
|
||||
}
|
||||
|
||||
pub fn pretty_coin(coin: &Coin) -> String {
|
||||
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
|
||||
let denom = if coin.denom.starts_with('u') {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::{info, warn};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::Coin;
|
||||
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||
use validator_client::nyxd::traits::MixnetSigningClient;
|
||||
@@ -14,7 +15,7 @@ pub struct Args {
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub signature: String,
|
||||
pub signature: MessageSignature,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
@@ -23,7 +24,7 @@ pub struct Args {
|
||||
pub clients_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub location: Option<String>,
|
||||
pub location: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub sphinx_key: String,
|
||||
@@ -59,9 +60,7 @@ pub async fn bond_gateway(args: Args, client: SigningClient) {
|
||||
host: args.host,
|
||||
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
|
||||
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
|
||||
location: args
|
||||
.location
|
||||
.unwrap_or_else(|| "secret gateway location".to_owned()),
|
||||
location: args.location,
|
||||
sphinx_key: args.sphinx_key,
|
||||
identity_key: args.identity_key,
|
||||
version: args.version,
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::utils::account_id_to_cw_addr;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Coin;
|
||||
use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload;
|
||||
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||
use validator_client::nyxd::traits::MixnetQueryClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub clients_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub location: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub sphinx_key: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub version: String,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
/// Indicates whether the gateway is going to get bonded via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
}
|
||||
|
||||
pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
let gateway = nym_mixnet_contract_common::Gateway {
|
||||
host: args.host,
|
||||
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
|
||||
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
|
||||
location: args.location,
|
||||
sphinx_key: args.sphinx_key,
|
||||
identity_key: args.identity_key,
|
||||
version: args.version,
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let nonce = match client.get_signing_nonce(client.address()).await {
|
||||
Ok(nonce) => nonce,
|
||||
Err(err) => {
|
||||
eprint!(
|
||||
"failed to query for the signing nonce of {}: {err}",
|
||||
client.address()
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(client.address());
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(client.vesting_contract_address()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload = construct_gateway_bonding_sign_payload(nonce, address, proxy, coin, gateway);
|
||||
println!("{}", payload.to_base58_string().unwrap())
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod bond_gateway;
|
||||
pub mod gateway_bonding_sign_payload;
|
||||
pub mod unbond_gateway;
|
||||
pub mod vesting_bond_gateway;
|
||||
pub mod vesting_unbond_gateway;
|
||||
@@ -25,4 +26,6 @@ pub enum MixnetOperatorsGatewayCommands {
|
||||
VestingBond(vesting_bond_gateway::Args),
|
||||
/// Unbound from a gateway (when originally using locked tokens)
|
||||
VestingUnbound(vesting_unbond_gateway::Args),
|
||||
/// Create base58-encoded payload required for producing valid bonding signature.
|
||||
CreateGatewayBondingSignPayload(gateway_bonding_sign_payload::Args),
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use log::{info, warn};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::{Coin, Gateway};
|
||||
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
|
||||
use validator_client::nyxd::VestingSigningClient;
|
||||
@@ -14,7 +15,7 @@ pub struct Args {
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub signature: String,
|
||||
pub signature: MessageSignature,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
@@ -23,7 +24,7 @@ pub struct Args {
|
||||
pub clients_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub location: Option<String>,
|
||||
pub location: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub sphinx_key: String,
|
||||
@@ -57,9 +58,7 @@ pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str
|
||||
host: args.host,
|
||||
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
|
||||
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
|
||||
location: args
|
||||
.location
|
||||
.unwrap_or_else(|| "secret gateway location".to_owned()),
|
||||
location: args.location,
|
||||
sphinx_key: args.sphinx_key,
|
||||
identity_key: args.identity_key,
|
||||
version: args.version,
|
||||
@@ -68,7 +67,7 @@ pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let res = client
|
||||
.vesting_bond_gateway(gateway, &args.signature, coin.into(), None)
|
||||
.vesting_bond_gateway(gateway, args.signature, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to bond gateway!");
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::{info, warn};
|
||||
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::{Coin, MixNodeCostParams, Percent};
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
@@ -20,7 +21,7 @@ pub struct Args {
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub signature: String,
|
||||
pub signature: MessageSignature,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use crate::utils::account_id_to_cw_addr;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::{Coin, Uint128};
|
||||
use nym_contracts_common::Percent;
|
||||
use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNodeCostParams};
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
use validator_client::nyxd::traits::MixnetQueryClient;
|
||||
use validator_client::nyxd::CosmWasmCoin;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub verloc_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub http_api_port: Option<u16>,
|
||||
|
||||
#[clap(long)]
|
||||
pub sphinx_key: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub version: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub interval_operating_cost: Option<u128>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
|
||||
)]
|
||||
pub amount: u128,
|
||||
|
||||
/// Indicates whether the mixnode is going to get bonded via a vesting account
|
||||
#[arg(long)]
|
||||
pub with_vesting_account: bool,
|
||||
}
|
||||
|
||||
pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
|
||||
let mixnode = nym_mixnet_contract_common::MixNode {
|
||||
host: args.host,
|
||||
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
|
||||
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
|
||||
http_api_port: args
|
||||
.http_api_port
|
||||
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
|
||||
sphinx_key: args.sphinx_key,
|
||||
identity_key: args.identity_key,
|
||||
version: args.version,
|
||||
};
|
||||
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let cost_params = MixNodeCostParams {
|
||||
profit_margin_percent: Percent::from_percentage_value(
|
||||
args.profit_margin_percent.unwrap_or(10) as u64,
|
||||
)
|
||||
.unwrap(),
|
||||
interval_operating_cost: CosmWasmCoin {
|
||||
denom: denom.into(),
|
||||
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
|
||||
},
|
||||
};
|
||||
|
||||
let nonce = match client.get_signing_nonce(client.address()).await {
|
||||
Ok(nonce) => nonce,
|
||||
Err(err) => {
|
||||
eprint!(
|
||||
"failed to query for the signing nonce of {}: {err}",
|
||||
client.address()
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(client.address());
|
||||
let proxy = if args.with_vesting_account {
|
||||
Some(account_id_to_cw_addr(client.vesting_contract_address()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload =
|
||||
construct_mixnode_bonding_sign_payload(nonce, address, proxy, coin, mixnode, cost_params);
|
||||
println!("{}", payload.to_base58_string().unwrap())
|
||||
}
|
||||
@@ -5,6 +5,7 @@ use clap::{Args, Subcommand};
|
||||
|
||||
pub mod bond_mixnode;
|
||||
pub mod keys;
|
||||
pub mod mixnode_bonding_sign_payload;
|
||||
pub mod rewards;
|
||||
pub mod settings;
|
||||
pub mod unbond_mixnode;
|
||||
@@ -34,4 +35,6 @@ pub enum MixnetOperatorsMixnodeCommands {
|
||||
BondVesting(vesting_bond_mixnode::Args),
|
||||
/// Unbound from a mixnode (when originally using locked tokens)
|
||||
UnboundVesting(vesting_unbond_mixnode::Args),
|
||||
/// Create base58-encoded payload required for producing valid bonding signature.
|
||||
CreateMixnodeBondingSignPayload(mixnode_bonding_sign_payload::Args),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::context::SigningClient;
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Uint128;
|
||||
use log::{info, warn};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_mixnet_contract_common::{Coin, MixNodeCostParams};
|
||||
use nym_mixnet_contract_common::{MixNode, Percent};
|
||||
use nym_network_defaults::{
|
||||
@@ -18,7 +19,7 @@ pub struct Args {
|
||||
pub host: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub signature: String,
|
||||
pub signature: MessageSignature,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_port: Option<u16>,
|
||||
@@ -95,7 +96,7 @@ pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str
|
||||
};
|
||||
|
||||
let res = client
|
||||
.vesting_bond_mixnode(mixnode, cost_params, &args.signature, coin.into(), None)
|
||||
.vesting_bond_mixnode(mixnode, cost_params, args.signature, coin.into(), None)
|
||||
.await
|
||||
.expect("failed to bond vesting mixnode!");
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
pub mod dealings;
|
||||
pub mod events;
|
||||
pub mod signing;
|
||||
pub mod types;
|
||||
|
||||
pub use types::*;
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{from_slice, to_vec, Addr, Coin, MessageInfo, StdResult};
|
||||
use schemars::JsonSchema;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
pub use verifier::Verifier;
|
||||
|
||||
pub mod verifier;
|
||||
|
||||
pub type Nonce = u32;
|
||||
|
||||
// define this type explicitly for [hopefully] better usability
|
||||
// (so you wouldn't need to worry about whether you should use bytes, bs58, etc.)
|
||||
#[derive(Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct MessageSignature(Vec<u8>);
|
||||
|
||||
impl MessageSignature {
|
||||
pub fn as_bs58_string(&self) -> String {
|
||||
bs58::encode(&self.0).into_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for MessageSignature {
|
||||
fn from(value: &'a [u8]) -> Self {
|
||||
MessageSignature(value.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for MessageSignature {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
MessageSignature(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for MessageSignature {
|
||||
type Error = bs58::decode::Error;
|
||||
|
||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||
Ok(MessageSignature(bs58::decode(value).into_vec()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for MessageSignature {
|
||||
type Error = bs58::decode::Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Self::try_from(value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for MessageSignature {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for MessageSignature {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let inner = String::deserialize(deserializer)?;
|
||||
let bytes = bs58::decode(inner).into_vec().map_err(de::Error::custom)?;
|
||||
Ok(MessageSignature(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for MessageSignature {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let bs58_encoded = self.as_bs58_string();
|
||||
bs58_encoded.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MessageSignature {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_bs58_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for MessageSignature {
|
||||
type Err = bs58::decode::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SigningPurpose {
|
||||
fn message_type() -> MessageType;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct MessageType(String);
|
||||
|
||||
impl MessageType {
|
||||
pub fn new<S: Into<String>>(typ: S) -> Self {
|
||||
MessageType(typ.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for MessageType
|
||||
where
|
||||
T: ToString,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
MessageType(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SigningAlgorithm {
|
||||
#[default]
|
||||
Ed25519,
|
||||
Secp256k1,
|
||||
}
|
||||
|
||||
impl SigningAlgorithm {
|
||||
pub fn is_ed25519(&self) -> bool {
|
||||
matches!(self, SigningAlgorithm::Ed25519)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: maybe move this one to repo-wide common?
|
||||
// TODO: should it perhaps also include the public key itself?
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SignableMessage<T> {
|
||||
pub nonce: u32,
|
||||
pub algorithm: SigningAlgorithm,
|
||||
pub message_type: MessageType,
|
||||
|
||||
pub content: T,
|
||||
}
|
||||
|
||||
impl<T> SignableMessage<T>
|
||||
where
|
||||
T: SigningPurpose,
|
||||
{
|
||||
pub fn new(nonce: u32, content: T) -> Self {
|
||||
SignableMessage {
|
||||
nonce,
|
||||
algorithm: SigningAlgorithm::Ed25519,
|
||||
message_type: T::message_type(),
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_signing_algorithm(mut self, algorithm: SigningAlgorithm) -> Self {
|
||||
self.algorithm = algorithm;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn to_plaintext(&self) -> StdResult<Vec<u8>>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
to_vec(self)
|
||||
}
|
||||
|
||||
pub fn to_sha256_plaintext_digest(&self) -> StdResult<Vec<u8>>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn to_json_string(&self) -> StdResult<String>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
// if you look into implementation of `serde_json_wasm::to_string` this [i.e. the String conversion]
|
||||
// CAN'T fail, but let's avoid this unnecessary unwrap either way
|
||||
self.to_plaintext()
|
||||
.map(|s| String::from_utf8(s).unwrap_or(String::from("SERIALIZATION FAILURE")))
|
||||
}
|
||||
|
||||
pub fn to_base58_string(&self) -> StdResult<String>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.to_plaintext().map(|s| bs58::encode(s).into_string())
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> StdResult<SignableMessage<T>>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
from_slice(bytes)
|
||||
}
|
||||
|
||||
pub fn try_from_string(raw: &str) -> StdResult<SignableMessage<T>>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
Self::try_from_bytes(raw.as_bytes())
|
||||
}
|
||||
|
||||
pub fn try_from_base58_string(raw: &str) -> bs58::decode::Result<StdResult<SignableMessage<T>>>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
bs58::decode(raw)
|
||||
.into_vec()
|
||||
.map(|d| Self::try_from_bytes(&d))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ContractMessageContent<T> {
|
||||
pub sender: Addr,
|
||||
pub proxy: Option<Addr>,
|
||||
pub funds: Vec<Coin>,
|
||||
pub data: T,
|
||||
}
|
||||
|
||||
impl<T> SigningPurpose for ContractMessageContent<T>
|
||||
where
|
||||
T: SigningPurpose,
|
||||
{
|
||||
fn message_type() -> MessageType {
|
||||
T::message_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ContractMessageContent<T> {
|
||||
pub fn new(sender: Addr, proxy: Option<Addr>, funds: Vec<Coin>, data: T) -> Self {
|
||||
ContractMessageContent {
|
||||
sender,
|
||||
proxy,
|
||||
funds,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_info(info: MessageInfo, signer: Addr, data: T) -> Self {
|
||||
let proxy = if info.sender == signer {
|
||||
None
|
||||
} else {
|
||||
Some(info.sender)
|
||||
};
|
||||
|
||||
ContractMessageContent {
|
||||
sender: signer,
|
||||
proxy,
|
||||
funds: info.funds,
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::signing::{MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose};
|
||||
use cosmwasm_std::{Api, StdError, VerificationError};
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
|
||||
pub trait Verifier {
|
||||
type Error: From<StdError>;
|
||||
|
||||
fn verify_message<T: Serialize + SigningPurpose>(
|
||||
&self,
|
||||
message: SignableMessage<T>,
|
||||
signature: MessageSignature,
|
||||
public_key: &[u8],
|
||||
) -> Result<bool, Self::Error> {
|
||||
match message.algorithm {
|
||||
SigningAlgorithm::Ed25519 => {
|
||||
let plaintext = message.to_plaintext()?;
|
||||
self.verify_ed25519(&plaintext, signature.as_ref(), public_key)
|
||||
}
|
||||
SigningAlgorithm::Secp256k1 => {
|
||||
let plaintext = message.to_sha256_plaintext_digest()?;
|
||||
self.verify_secp256k1(&plaintext, signature.as_ref(), public_key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_ed25519(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
_signature: &[u8],
|
||||
_public_key: &[u8],
|
||||
) -> Result<bool, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn verify_secp256k1(
|
||||
&self,
|
||||
_message_hash: &[u8],
|
||||
_signature: &[u8],
|
||||
_public_key: &[u8],
|
||||
) -> Result<bool, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
pub enum ApiVerifierError {
|
||||
#[error(transparent)]
|
||||
Verification(#[from] VerificationError),
|
||||
|
||||
#[error(transparent)]
|
||||
Std(#[from] StdError),
|
||||
}
|
||||
|
||||
impl<T> Verifier for T
|
||||
where
|
||||
T: Api + ?Sized,
|
||||
{
|
||||
type Error = ApiVerifierError;
|
||||
|
||||
fn verify_ed25519(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
public_key: &[u8],
|
||||
) -> Result<bool, Self::Error> {
|
||||
Ok(self.ed25519_verify(message, signature, public_key)?)
|
||||
}
|
||||
|
||||
fn verify_secp256k1(
|
||||
&self,
|
||||
message_hash: &[u8],
|
||||
signature: &[u8],
|
||||
public_key: &[u8],
|
||||
) -> Result<bool, Self::Error> {
|
||||
Ok(self.secp256k1_verify(message_hash, signature, public_key)?)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{EpochState, IdentityKey, MixId};
|
||||
use contracts_common::signing::verifier::ApiVerifierError;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -218,4 +219,10 @@ pub enum MixnetContractError {
|
||||
value: String,
|
||||
error_message: String,
|
||||
},
|
||||
|
||||
#[error("failed to verify message signature: {source}")]
|
||||
SignatureVerificationFailure {
|
||||
#[from]
|
||||
source: ApiVerifierError,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ mod msg;
|
||||
pub mod pending_events;
|
||||
pub mod reward_params;
|
||||
pub mod rewarding;
|
||||
pub mod signing_types;
|
||||
mod types;
|
||||
|
||||
pub use contracts_common::types::*;
|
||||
@@ -43,4 +44,5 @@ pub use pending_events::{
|
||||
PendingIntervalEventData, PendingIntervalEventKind,
|
||||
};
|
||||
pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams};
|
||||
pub use signing_types::*;
|
||||
pub use types::*;
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::reward_params::{
|
||||
};
|
||||
use crate::{delegation, ContractStateParams, Layer, LayerAssignment, MixId, Percent};
|
||||
use crate::{Gateway, IdentityKey, MixNode};
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use cosmwasm_std::Decimal;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -153,12 +154,12 @@ pub enum ExecuteMsg {
|
||||
BondMixnode {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
},
|
||||
BondMixnodeOnBehalf {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
owner: String,
|
||||
},
|
||||
PledgeMore {},
|
||||
@@ -187,12 +188,12 @@ pub enum ExecuteMsg {
|
||||
// gateway-related:
|
||||
BondGateway {
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
},
|
||||
BondGatewayOnBehalf {
|
||||
gateway: Gateway,
|
||||
owner: String,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
},
|
||||
UnbondGateway {},
|
||||
UnbondGatewayOnBehalf {
|
||||
@@ -500,6 +501,11 @@ pub enum QueryMsg {
|
||||
start_after: Option<u32>,
|
||||
},
|
||||
GetNumberOfPendingEvents {},
|
||||
|
||||
// signing-related
|
||||
GetSigningNonce {
|
||||
address: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{Gateway, MixNode, MixNodeCostParams};
|
||||
use contracts_common::signing::{
|
||||
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
|
||||
};
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use serde::Serialize;
|
||||
|
||||
pub type SignableMixNodeBondingMsg = SignableMessage<ContractMessageContent<MixnodeBondingPayload>>;
|
||||
pub type SignableGatewayBondingMsg = SignableMessage<ContractMessageContent<GatewayBondingPayload>>;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MixnodeBondingPayload {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
}
|
||||
|
||||
impl MixnodeBondingPayload {
|
||||
pub fn new(mix_node: MixNode, cost_params: MixNodeCostParams) -> Self {
|
||||
Self {
|
||||
mix_node,
|
||||
cost_params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningPurpose for MixnodeBondingPayload {
|
||||
fn message_type() -> MessageType {
|
||||
MessageType::new("mixnode-bonding")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn construct_mixnode_bonding_sign_payload(
|
||||
nonce: Nonce,
|
||||
sender: Addr,
|
||||
proxy: Option<Addr>,
|
||||
pledge: Coin,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
) -> SignableMixNodeBondingMsg {
|
||||
let payload = MixnodeBondingPayload::new(mix_node, cost_params);
|
||||
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
|
||||
|
||||
SignableMessage::new(nonce, content)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct GatewayBondingPayload {
|
||||
gateway: Gateway,
|
||||
}
|
||||
|
||||
impl GatewayBondingPayload {
|
||||
pub fn new(gateway: Gateway) -> Self {
|
||||
Self { gateway }
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningPurpose for GatewayBondingPayload {
|
||||
fn message_type() -> MessageType {
|
||||
MessageType::new("gateway-bonding")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn construct_gateway_bonding_sign_payload(
|
||||
nonce: Nonce,
|
||||
sender: Addr,
|
||||
proxy: Option<Addr>,
|
||||
pledge: Coin,
|
||||
gateway: Gateway,
|
||||
) -> SignableGatewayBondingMsg {
|
||||
let payload = GatewayBondingPayload::new(gateway);
|
||||
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
|
||||
|
||||
SignableMessage::new(nonce, content)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FamilyCreationSignature {
|
||||
label: String,
|
||||
// TODO: add any extra fields?
|
||||
}
|
||||
|
||||
impl FamilyCreationSignature {
|
||||
pub fn new(label: String) -> Self {
|
||||
Self { label }
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningPurpose for FamilyCreationSignature {
|
||||
fn message_type() -> MessageType {
|
||||
MessageType::new("family-creation")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FamilyJoinSignature {
|
||||
family_head: String,
|
||||
// TODO: add any extra fields?
|
||||
}
|
||||
|
||||
impl FamilyJoinSignature {
|
||||
pub fn new(family_head: String) -> Self {
|
||||
Self { family_head }
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningPurpose for FamilyJoinSignature {
|
||||
fn message_type() -> MessageType {
|
||||
MessageType::new("family-join")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FamilyLeaveSignature {
|
||||
family_head: String,
|
||||
// TODO: add any extra fields?
|
||||
}
|
||||
|
||||
impl FamilyLeaveSignature {
|
||||
pub fn new(family_head: String) -> Self {
|
||||
Self { family_head }
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningPurpose for FamilyLeaveSignature {
|
||||
fn message_type() -> MessageType {
|
||||
MessageType::new("family-leave")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FamilyKickSignature {
|
||||
member: String,
|
||||
// TODO: add any extra fields?
|
||||
}
|
||||
|
||||
impl FamilyKickSignature {
|
||||
pub fn new(member: String) -> Self {
|
||||
Self { member }
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningPurpose for FamilyKickSignature {
|
||||
fn message_type() -> MessageType {
|
||||
MessageType::new("family-member-removal")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: depending on our threat model, we should perhaps extend it to include all _on_behalf methods
|
||||
@@ -1,3 +1,4 @@
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use cosmwasm_std::{Coin, Timestamp};
|
||||
use mixnet_contract_common::{
|
||||
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
|
||||
@@ -118,7 +119,7 @@ pub enum ExecuteMsg {
|
||||
BondMixnode {
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
amount: Coin,
|
||||
},
|
||||
PledgeMore {
|
||||
@@ -131,7 +132,7 @@ pub enum ExecuteMsg {
|
||||
},
|
||||
BondGateway {
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
amount: Coin,
|
||||
},
|
||||
UnbondGateway {},
|
||||
|
||||
@@ -81,7 +81,6 @@ impl GatewayBond {
|
||||
pub struct GatewayNodeDetailsResponse {
|
||||
pub identity_key: String,
|
||||
pub sphinx_key: String,
|
||||
pub owner_signature: String,
|
||||
pub announce_address: String,
|
||||
pub bind_address: String,
|
||||
pub version: String,
|
||||
@@ -94,7 +93,6 @@ impl fmt::Display for GatewayNodeDetailsResponse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "Identity Key: {}", self.identity_key)?;
|
||||
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
|
||||
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Host: {} (bind address: {})",
|
||||
|
||||
@@ -167,7 +167,6 @@ impl MixNodeCostParams {
|
||||
pub struct MixnodeNodeDetailsResponse {
|
||||
pub identity_key: String,
|
||||
pub sphinx_key: String,
|
||||
pub owner_signature: String,
|
||||
pub announce_address: String,
|
||||
pub bind_address: String,
|
||||
pub version: String,
|
||||
@@ -182,7 +181,6 @@ impl fmt::Display for MixnodeNodeDetailsResponse {
|
||||
let wallet_address = self.wallet_address.clone().unwrap_or_default();
|
||||
writeln!(f, "Identity Key: {}", self.identity_key)?;
|
||||
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
|
||||
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Host: {} (bind address: {})",
|
||||
|
||||
@@ -9,7 +9,6 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
nym-bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
|
||||
nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ publish = false
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nym-bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
|
||||
nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||
|
||||
@@ -24,7 +24,7 @@ crate-type = ["cdylib", "rlib"]
|
||||
[dependencies]
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.2.0" }
|
||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.2.0" }
|
||||
#nym-config = { path = "../../common/config"}
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.2.0" }
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
@@ -41,7 +41,6 @@ semver = { version = "1.0.16", default-features = false }
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = "1.0.0"
|
||||
rand_chacha = "0.2"
|
||||
#rand = "0.7"
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -39,41 +39,43 @@ pub const FAMILIES_DEFAULT_RETRIEVAL_LIMIT: u32 = 10;
|
||||
pub const FAMILIES_MAX_RETRIEVAL_LIMIT: u32 = 20;
|
||||
|
||||
// storage keys
|
||||
pub(crate) const DELEGATION_PK_NAMESPACE: &str = "dl";
|
||||
pub(crate) const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
|
||||
pub(crate) const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
|
||||
pub const DELEGATION_PK_NAMESPACE: &str = "dl";
|
||||
pub const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
|
||||
pub const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
|
||||
|
||||
pub(crate) const GATEWAYS_PK_NAMESPACE: &str = "gt";
|
||||
pub(crate) const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
|
||||
pub const GATEWAYS_PK_NAMESPACE: &str = "gt";
|
||||
pub const GATEWAYS_OWNER_IDX_NAMESPACE: &str = "gto";
|
||||
|
||||
pub(crate) const REWARDED_SET_KEY: &str = "rs";
|
||||
pub(crate) const CURRENT_EPOCH_STATUS_KEY: &str = "ces";
|
||||
pub(crate) const CURRENT_INTERVAL_KEY: &str = "ci";
|
||||
pub(crate) const EPOCH_EVENT_ID_COUNTER_KEY: &str = "eic";
|
||||
pub(crate) const INTERVAL_EVENT_ID_COUNTER_KEY: &str = "iic";
|
||||
pub(crate) const PENDING_EPOCH_EVENTS_NAMESPACE: &str = "pee";
|
||||
pub(crate) const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
|
||||
pub const REWARDED_SET_KEY: &str = "rs";
|
||||
pub const CURRENT_EPOCH_STATUS_KEY: &str = "ces";
|
||||
pub const CURRENT_INTERVAL_KEY: &str = "ci";
|
||||
pub const EPOCH_EVENT_ID_COUNTER_KEY: &str = "eic";
|
||||
pub const INTERVAL_EVENT_ID_COUNTER_KEY: &str = "iic";
|
||||
pub const PENDING_EPOCH_EVENTS_NAMESPACE: &str = "pee";
|
||||
pub const PENDING_INTERVAL_EVENTS_NAMESPACE: &str = "pie";
|
||||
|
||||
pub(crate) const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
|
||||
pub(crate) const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
|
||||
pub const LAST_EPOCH_EVENT_ID_KEY: &str = "lee";
|
||||
pub const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
|
||||
|
||||
pub(crate) const CONTRACT_STATE_KEY: &str = "state";
|
||||
pub const CONTRACT_STATE_KEY: &str = "state";
|
||||
|
||||
pub(crate) const LAYER_DISTRIBUTION_KEY: &str = "layers";
|
||||
pub(crate) const NODE_ID_COUNTER_KEY: &str = "nic";
|
||||
pub(crate) const MIXNODES_PK_NAMESPACE: &str = "mnn";
|
||||
pub(crate) const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
|
||||
pub(crate) const MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "mni";
|
||||
pub(crate) const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
|
||||
pub const LAYER_DISTRIBUTION_KEY: &str = "layers";
|
||||
pub const NODE_ID_COUNTER_KEY: &str = "nic";
|
||||
pub const MIXNODES_PK_NAMESPACE: &str = "mnn";
|
||||
pub const MIXNODES_OWNER_IDX_NAMESPACE: &str = "mno";
|
||||
pub const MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "mni";
|
||||
pub const MIXNODES_SPHINX_IDX_NAMESPACE: &str = "mns";
|
||||
|
||||
pub(crate) const UNBONDED_MIXNODES_PK_NAMESPACE: &str = "ubm";
|
||||
pub(crate) const UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE: &str = "umo";
|
||||
pub(crate) const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi";
|
||||
pub const UNBONDED_MIXNODES_PK_NAMESPACE: &str = "ubm";
|
||||
pub const UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE: &str = "umo";
|
||||
pub const UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE: &str = "umi";
|
||||
|
||||
pub(crate) const REWARDING_PARAMS_KEY: &str = "rparams";
|
||||
pub(crate) const PENDING_REWARD_POOL_KEY: &str = "prp";
|
||||
pub(crate) const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
|
||||
pub const REWARDING_PARAMS_KEY: &str = "rparams";
|
||||
pub const PENDING_REWARD_POOL_KEY: &str = "prp";
|
||||
pub const MIXNODES_REWARDING_PK_NAMESPACE: &str = "mnr";
|
||||
|
||||
pub(crate) const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
|
||||
pub(crate) const FAMILIES_MAP_NAMESPACE: &str = "fam2";
|
||||
pub(crate) const MEMBERS_MAP_NAMESPACE: &str = "memb2";
|
||||
pub const FAMILIES_INDEX_NAMESPACE: &str = "faml2";
|
||||
pub const FAMILIES_MAP_NAMESPACE: &str = "fam2";
|
||||
pub const MEMBERS_MAP_NAMESPACE: &str = "memb2";
|
||||
|
||||
pub const SIGNING_NONCES_NAMESPACE: &str = "sn";
|
||||
|
||||
@@ -581,6 +581,9 @@ pub fn query(
|
||||
QueryMsg::GetNumberOfPendingEvents {} => to_binary(
|
||||
&crate::interval::queries::query_number_of_pending_events(deps)?,
|
||||
),
|
||||
QueryMsg::GetSigningNonce { address } => to_binary(
|
||||
&crate::signing::queries::query_current_signing_nonce(deps, address)?,
|
||||
),
|
||||
};
|
||||
|
||||
Ok(query_res?)
|
||||
|
||||
@@ -305,71 +305,77 @@ mod test {
|
||||
use crate::families::queries::{get_family_by_head, get_family_by_label};
|
||||
use crate::families::storage::is_family_member;
|
||||
use crate::mixnet_contract_settings::storage::minimum_mixnode_pledge;
|
||||
use crate::support::tests::{fixtures, test_helpers};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use crate::support::tests::fixtures;
|
||||
use crate::support::tests::test_helpers::TestSetup;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
|
||||
#[test]
|
||||
fn test_family_crud() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
let env = test.env();
|
||||
|
||||
let head = "alice";
|
||||
let malicious_head = "timmy";
|
||||
let member = "bob";
|
||||
|
||||
let minimum_pledge = minimum_mixnode_pledge(deps.as_ref().storage).unwrap();
|
||||
|
||||
let (head_mixnode, head_sig, head_keypair) =
|
||||
test_helpers::mixnode_with_signature(&mut rng, head);
|
||||
|
||||
let (malicious_mixnode, malicious_sig, _malicious_keypair) =
|
||||
test_helpers::mixnode_with_signature(&mut rng, malicious_head);
|
||||
|
||||
let minimum_pledge = minimum_mixnode_pledge(test.deps().storage).unwrap();
|
||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||
|
||||
let member = "bob";
|
||||
let (member_mixnode, member_sig, _) =
|
||||
test_helpers::mixnode_with_signature(&mut rng, member);
|
||||
let (head_mixnode, head_bond_sig, head_keypair) = test.mixnode_with_signature(head, None);
|
||||
let (malicious_mixnode, malicious_bond_sig, malicious_keypair) =
|
||||
test.mixnode_with_signature(malicious_head, None);
|
||||
let (member_mixnode, member_bond_sig, member_keypair) =
|
||||
test.mixnode_with_signature(member, None);
|
||||
|
||||
// we are informed that we didn't send enough funds
|
||||
crate::mixnodes::transactions::try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
mock_info(head, &[minimum_pledge.clone()]),
|
||||
head_mixnode.clone(),
|
||||
cost_params.clone(),
|
||||
head_sig.clone(),
|
||||
head_bond_sig,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
crate::mixnodes::transactions::try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
mock_info(malicious_head, &[minimum_pledge.clone()]),
|
||||
malicious_mixnode,
|
||||
cost_params.clone(),
|
||||
malicious_sig.clone(),
|
||||
malicious_bond_sig,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
crate::mixnodes::transactions::try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
env,
|
||||
mock_info(member, &[minimum_pledge]),
|
||||
member_mixnode.clone(),
|
||||
cost_params,
|
||||
member_sig.clone(),
|
||||
member_bond_sig,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
try_create_family(deps.as_mut(), mock_info(head, &[]), head_sig, "test").unwrap();
|
||||
let old_style_head_sig = head_keypair.private_key().sign_text(head);
|
||||
let old_style_malicious_head_sig =
|
||||
malicious_keypair.private_key().sign_text(malicious_head);
|
||||
let old_style_member_sig = member_keypair.private_key().sign_text(member);
|
||||
|
||||
try_create_family(
|
||||
test.deps_mut(),
|
||||
mock_info(head, &[]),
|
||||
old_style_head_sig,
|
||||
"test",
|
||||
)
|
||||
.unwrap();
|
||||
let family_head = FamilyHead::new(&head_mixnode.identity_key);
|
||||
assert!(get_family(&family_head, &deps.storage).is_ok());
|
||||
assert!(get_family(&family_head, test.deps().storage).is_ok());
|
||||
|
||||
let nope = try_create_family(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
mock_info(malicious_head, &[]),
|
||||
malicious_sig,
|
||||
old_style_malicious_head_sig,
|
||||
"test",
|
||||
);
|
||||
|
||||
@@ -381,11 +387,11 @@ mod test {
|
||||
},
|
||||
}
|
||||
|
||||
let family = get_family_by_label("test", &deps.storage).unwrap();
|
||||
let family = get_family_by_label("test", test.deps().storage).unwrap();
|
||||
assert!(family.is_some());
|
||||
assert_eq!(family.unwrap().head_identity(), family_head.identity());
|
||||
|
||||
let family = get_family_by_head(family_head.identity(), &deps.storage).unwrap();
|
||||
let family = get_family_by_head(family_head.identity(), test.deps().storage).unwrap();
|
||||
assert_eq!(family.head_identity(), family_head.identity());
|
||||
|
||||
let join_signature = head_keypair
|
||||
@@ -394,41 +400,47 @@ mod test {
|
||||
.to_base58_string();
|
||||
|
||||
try_join_family(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
mock_info(member, &[]),
|
||||
Some(member_sig.clone()),
|
||||
Some(old_style_member_sig.clone()),
|
||||
join_signature.clone(),
|
||||
head_mixnode.identity_key.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let family = get_family(&family_head, &deps.storage).unwrap();
|
||||
let family = get_family(&family_head, test.deps().storage).unwrap();
|
||||
|
||||
assert!(is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
|
||||
assert!(
|
||||
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
|
||||
);
|
||||
|
||||
try_leave_family(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
mock_info(member, &[]),
|
||||
member_sig.clone(),
|
||||
old_style_member_sig.clone(),
|
||||
head_mixnode.identity_key.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let family = get_family(&family_head, &deps.storage).unwrap();
|
||||
assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
|
||||
let family = get_family(&family_head, test.deps().storage).unwrap();
|
||||
assert!(
|
||||
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
|
||||
);
|
||||
|
||||
try_join_family(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
mock_info(member, &[]),
|
||||
Some(member_sig.clone()),
|
||||
join_signature.clone(),
|
||||
head_mixnode.identity_key.clone(),
|
||||
Some(old_style_member_sig),
|
||||
join_signature,
|
||||
head_mixnode.identity_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let family = get_family(&family_head, &deps.storage).unwrap();
|
||||
let family = get_family(&family_head, test.deps().storage).unwrap();
|
||||
|
||||
assert!(is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
|
||||
assert!(
|
||||
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
|
||||
);
|
||||
|
||||
// try_head_kick_member(
|
||||
// deps.as_mut(),
|
||||
@@ -438,8 +450,8 @@ mod test {
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// let family = get_family(&family_head, &deps.storage).unwrap();
|
||||
// assert!(!is_family_member(&deps.storage, &family, &member_mixnode.identity_key).unwrap());
|
||||
// let family = get_family(&family_head, test.deps().storage).unwrap();
|
||||
// assert!(!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -456,7 +468,7 @@ mod test {
|
||||
|
||||
let head = "alice";
|
||||
|
||||
let (_, keypair) = test.add_dummy_mixnode_with_keypair(head, None);
|
||||
let (_, keypair) = test.add_dummy_mixnode_with_proxy_and_keypair(head, None);
|
||||
let sig = keypair.private_key().sign_text(head);
|
||||
|
||||
let res = try_create_family_on_behalf(
|
||||
@@ -495,7 +507,7 @@ mod test {
|
||||
let new_member = "vin-diesel";
|
||||
|
||||
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
||||
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
|
||||
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
|
||||
|
||||
// TODO: those signatures are WRONG and have to be c hanged
|
||||
let join_signature = head_keys
|
||||
@@ -542,7 +554,7 @@ mod test {
|
||||
let new_member = "vin-diesel";
|
||||
|
||||
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
||||
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
|
||||
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
|
||||
|
||||
// TODO: those signatures are WRONG and have to be changed
|
||||
let join_signature = head_keys
|
||||
@@ -599,7 +611,7 @@ mod test {
|
||||
let new_member = "vin-diesel";
|
||||
|
||||
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
|
||||
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
|
||||
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
|
||||
|
||||
// TODO: those signatures are WRONG and have to be c hanged
|
||||
let join_signature = head_keys
|
||||
@@ -613,7 +625,7 @@ mod test {
|
||||
test.deps_mut(),
|
||||
mock_info(vesting_contract.as_ref(), &[]),
|
||||
new_member.to_string(),
|
||||
Some(member_sig.clone()),
|
||||
Some(member_sig),
|
||||
join_signature,
|
||||
head_identity,
|
||||
)
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod queries;
|
||||
pub mod signature_helpers;
|
||||
pub mod storage;
|
||||
pub mod transactions;
|
||||
|
||||
@@ -62,6 +62,7 @@ pub(crate) mod tests {
|
||||
use crate::contract::execute;
|
||||
use crate::support::tests;
|
||||
use crate::support::tests::test_helpers;
|
||||
use crate::support::tests::test_helpers::TestSetup;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
|
||||
#[test]
|
||||
@@ -73,27 +74,22 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn gateways_paged_retrieval_obeys_limits() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_gateways(1000);
|
||||
|
||||
let limit = 2;
|
||||
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
|
||||
|
||||
let page1 = query_gateways_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
|
||||
let page1 = query_gateways_paged(test.deps(), None, Option::from(limit)).unwrap();
|
||||
assert_eq!(limit, page1.nodes.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateways_paged_retrieval_has_default_limit() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
|
||||
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_gateways(1000);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_gateways_paged(deps.as_ref(), None, None).unwrap();
|
||||
let page1 = query_gateways_paged(test.deps(), None, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
GATEWAY_BOND_DEFAULT_RETRIEVAL_LIMIT,
|
||||
@@ -103,15 +99,12 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn gateways_paged_retrieval_has_max_limit() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
|
||||
test_helpers::add_dummy_gateways(&mut rng, deps.as_mut(), env, 1000);
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_gateways(1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * GATEWAY_BOND_DEFAULT_RETRIEVAL_LIMIT;
|
||||
let page1 = query_gateways_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap();
|
||||
let page1 = query_gateways_paged(test.deps(), None, Option::from(crazy_limit)).unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
let expected_limit = GATEWAY_BOND_MAX_RETRIEVAL_LIMIT;
|
||||
@@ -121,25 +114,27 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn gateway_pagination_works() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let _env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let stake = tests::fixtures::good_gateway_pledge();
|
||||
|
||||
// prepare 4 messages and identities that are sorted by the generated identities
|
||||
// (because we query them in an ascended manner)
|
||||
let mut exec_data = (0..4)
|
||||
.map(|i| {
|
||||
let sender = format!("nym-addr{}", i);
|
||||
let (msg, identity) = tests::messages::valid_bond_gateway_msg(&mut rng, &sender);
|
||||
let (msg, identity) = tests::messages::valid_bond_gateway_msg(
|
||||
&mut rng,
|
||||
deps.as_ref(),
|
||||
stake.clone(),
|
||||
&sender,
|
||||
);
|
||||
(msg, (sender, identity))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
exec_data.sort_by(|(_, (_, id1)), (_, (_, id2))| id1.cmp(id2));
|
||||
let (messages, sender_identities): (Vec<_>, Vec<_>) = exec_data.into_iter().unzip();
|
||||
|
||||
let info = mock_info(
|
||||
&sender_identities[0].0.clone(),
|
||||
&tests::fixtures::good_gateway_pledge(),
|
||||
);
|
||||
let info = mock_info(&sender_identities[0].0.clone(), &stake);
|
||||
execute(deps.as_mut(), mock_env(), info, messages[0].clone()).unwrap();
|
||||
|
||||
let per_page = 2;
|
||||
@@ -200,43 +195,29 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn query_for_gateway_owner_works() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
// "fred" does not own a mixnode if there are no mixnodes
|
||||
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
|
||||
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
|
||||
assert!(res.gateway.is_none());
|
||||
|
||||
// mixnode was added to "bob", "fred" still does not own one
|
||||
test_helpers::add_gateway(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
"bob",
|
||||
tests::fixtures::good_gateway_pledge(),
|
||||
);
|
||||
// gateway was added to "bob", "fred" still does not own one
|
||||
test.add_dummy_gateway("bob", None);
|
||||
|
||||
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
|
||||
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
|
||||
assert!(res.gateway.is_none());
|
||||
|
||||
// "fred" now owns a gateway!
|
||||
test_helpers::add_gateway(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env,
|
||||
"fred",
|
||||
tests::fixtures::good_gateway_pledge(),
|
||||
);
|
||||
test.add_dummy_gateway("fred", None);
|
||||
|
||||
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
|
||||
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
|
||||
assert!(res.gateway.is_some());
|
||||
|
||||
// but after unbonding it, he doesn't own one anymore
|
||||
crate::gateways::transactions::try_remove_gateway(deps.as_mut(), mock_info("fred", &[]))
|
||||
crate::gateways::transactions::try_remove_gateway(test.deps_mut(), mock_info("fred", &[]))
|
||||
.unwrap();
|
||||
|
||||
let res = query_owned_gateway(deps.as_ref(), "fred".to_string()).unwrap();
|
||||
let res = query_owned_gateway(test.deps(), "fred".to_string()).unwrap();
|
||||
assert!(res.gateway.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::signing::storage as signing_storage;
|
||||
use cosmwasm_std::{Addr, Coin, Deps};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::{construct_gateway_bonding_sign_payload, Gateway};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_contracts_common::signing::Verifier;
|
||||
|
||||
pub(crate) fn verify_gateway_bonding_signature(
|
||||
deps: Deps<'_>,
|
||||
sender: Addr,
|
||||
proxy: Option<Addr>,
|
||||
pledge: Coin,
|
||||
gateway: Gateway,
|
||||
signature: MessageSignature,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
// recover the public key
|
||||
let mut public_key = [0u8; 32];
|
||||
bs58::decode(&gateway.identity_key)
|
||||
.into(&mut public_key)
|
||||
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
|
||||
|
||||
// reconstruct the payload
|
||||
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
|
||||
let msg = construct_gateway_bonding_sign_payload(nonce, sender, proxy, pledge, gateway);
|
||||
|
||||
if deps.api.verify_message(msg, signature, &public_key)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MixnetContractError::InvalidEd25519Signature)
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::storage;
|
||||
use crate::gateways::signature_helpers::verify_gateway_bonding_signature;
|
||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use crate::signing::storage as signing_storage;
|
||||
use crate::support::helpers::{
|
||||
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_node_identity_signature,
|
||||
validate_pledge,
|
||||
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_pledge,
|
||||
};
|
||||
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
|
||||
use mixnet_contract_common::{Gateway, GatewayBond};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
|
||||
pub fn try_add_gateway(
|
||||
@@ -18,7 +20,7 @@ pub fn try_add_gateway(
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
_try_add_gateway(
|
||||
deps,
|
||||
@@ -37,7 +39,7 @@ pub fn try_add_gateway_on_behalf(
|
||||
info: MessageInfo,
|
||||
gateway: Gateway,
|
||||
owner: String,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
ensure_sent_by_vesting_contract(&info, deps.storage)?;
|
||||
|
||||
@@ -54,13 +56,15 @@ pub fn try_add_gateway_on_behalf(
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: perhaps also require the user to explicitly provide what it thinks is the current nonce
|
||||
// so that we could return a better error message if it doesn't match?
|
||||
pub(crate) fn _try_add_gateway(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
gateway: Gateway,
|
||||
pledge: Vec<Coin>,
|
||||
owner: Addr,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
// check if the pledge contains any funds of the appropriate denomination
|
||||
@@ -82,13 +86,18 @@ pub(crate) fn _try_add_gateway(
|
||||
}
|
||||
|
||||
// check if this sender actually owns the gateway by checking the signature
|
||||
validate_node_identity_signature(
|
||||
verify_gateway_bonding_signature(
|
||||
deps.as_ref(),
|
||||
&owner,
|
||||
&owner_signature,
|
||||
&gateway.identity_key,
|
||||
owner.clone(),
|
||||
proxy.clone(),
|
||||
pledge.clone(),
|
||||
gateway.clone(),
|
||||
owner_signature,
|
||||
)?;
|
||||
|
||||
// update the signing nonce associated with this sender so that the future signature would be made on the new value
|
||||
signing_storage::increment_signing_nonce(deps.storage, owner.clone())?;
|
||||
|
||||
let gateway_identity = gateway.identity_key.clone();
|
||||
let bond = GatewayBond::new(
|
||||
pledge.clone(),
|
||||
@@ -182,39 +191,41 @@ pub(crate) fn _try_remove_gateway(
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::contract::execute;
|
||||
use crate::gateways::queries;
|
||||
use crate::gateways::transactions::{
|
||||
try_add_gateway, try_add_gateway_on_behalf, try_remove_gateway_on_behalf,
|
||||
};
|
||||
use crate::interval::pending_events;
|
||||
use crate::mixnet_contract_settings::storage::minimum_gateway_pledge;
|
||||
use crate::support::tests;
|
||||
use crate::support::tests::fixtures::{good_gateway_pledge, TEST_COIN_DENOM};
|
||||
use crate::support::tests::fixtures;
|
||||
use crate::support::tests::fixtures::{good_gateway_pledge, good_mixnode_pledge};
|
||||
use crate::support::tests::test_helpers::TestSetup;
|
||||
use crate::support::tests::{fixtures, test_helpers};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::{coin, Addr, BankMsg, Response, Uint128};
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::{Addr, BankMsg, Response, Uint128};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::events::new_gateway_unbonding_event;
|
||||
use mixnet_contract_common::ExecuteMsg;
|
||||
|
||||
#[test]
|
||||
fn gateway_add() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
// if we fail validation (by say not sending enough funds
|
||||
let sender = "alice";
|
||||
let minimum_pledge = minimum_gateway_pledge(deps.as_ref().storage).unwrap();
|
||||
let minimum_pledge = minimum_gateway_pledge(test.deps().storage).unwrap();
|
||||
let mut insufficient_pledge = minimum_pledge.clone();
|
||||
insufficient_pledge.amount -= Uint128::new(1000);
|
||||
|
||||
let info = mock_info(sender, &[insufficient_pledge.clone()]);
|
||||
let (gateway, sig) = test_helpers::gateway_with_signature(&mut rng, sender);
|
||||
let (gateway, sig) =
|
||||
test.gateway_with_signature(sender, Some(vec![insufficient_pledge.clone()]));
|
||||
|
||||
let env = test.env();
|
||||
let result = try_add_gateway(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info,
|
||||
gateway.clone(),
|
||||
@@ -233,47 +244,23 @@ pub mod tests {
|
||||
// if the signature provided is invalid, the bonding also fails
|
||||
let info = mock_info(sender, &[minimum_pledge]);
|
||||
|
||||
let result = try_add_gateway(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
gateway.clone(),
|
||||
"bad-signature".into(),
|
||||
);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(MixnetContractError::MalformedEd25519Signature(..))
|
||||
));
|
||||
|
||||
// if there was already a gateway bonded by particular user
|
||||
test_helpers::add_gateway(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
sender,
|
||||
fixtures::good_gateway_pledge(),
|
||||
);
|
||||
test.add_dummy_gateway(sender, None);
|
||||
|
||||
// it fails
|
||||
let result = try_add_gateway(deps.as_mut(), env.clone(), info, gateway, sig);
|
||||
let result = try_add_gateway(test.deps_mut(), env.clone(), info, gateway, sig);
|
||||
assert_eq!(Err(MixnetContractError::AlreadyOwnsGateway), result);
|
||||
|
||||
// the same holds if the user already owns a mixnode
|
||||
let sender2 = "mixnode-owner";
|
||||
|
||||
let mix_id = test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
sender2,
|
||||
vec![coin(100_000_000, TEST_COIN_DENOM)],
|
||||
);
|
||||
let mix_id = test.add_dummy_mixnode(sender2, None);
|
||||
|
||||
let info = mock_info(sender2, &fixtures::good_gateway_pledge());
|
||||
let (gateway, sig) = test_helpers::gateway_with_signature(&mut rng, sender2);
|
||||
let (gateway, sig) = test.gateway_with_signature(sender2, None);
|
||||
|
||||
let result = try_add_gateway(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
gateway.clone(),
|
||||
@@ -282,12 +269,84 @@ pub mod tests {
|
||||
assert_eq!(Err(MixnetContractError::AlreadyOwnsMixnode), result);
|
||||
|
||||
// but after he unbonds it, it's all fine again
|
||||
pending_events::unbond_mixnode(deps.as_mut(), &env, 123, mix_id).unwrap();
|
||||
pending_events::unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
|
||||
|
||||
let result = try_add_gateway(deps.as_mut(), env, info, gateway, sig);
|
||||
let result = try_add_gateway(test.deps_mut(), env, info, gateway, sig);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_gateway_with_invalid_signatures() {
|
||||
let mut test = TestSetup::new();
|
||||
let env = test.env();
|
||||
|
||||
let sender = "alice";
|
||||
let pledge = good_mixnode_pledge();
|
||||
let info = mock_info(sender, pledge.as_ref());
|
||||
|
||||
let (gateway, signature) = test.gateway_with_signature(sender, Some(pledge.clone()));
|
||||
|
||||
// using different parameters than what the signature was made on
|
||||
let mut modified_gateway = gateway.clone();
|
||||
modified_gateway.mix_port += 1;
|
||||
let res = try_add_gateway(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info,
|
||||
modified_gateway,
|
||||
signature.clone(),
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||
|
||||
// even stake amount is protected
|
||||
let mut different_pledge = pledge.clone();
|
||||
different_pledge[0].amount += Uint128::new(12345);
|
||||
|
||||
let info = mock_info(sender, different_pledge.as_ref());
|
||||
let res = try_add_gateway(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
gateway.clone(),
|
||||
signature.clone(),
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||
|
||||
let other_sender = mock_info("another-sender", pledge.as_ref());
|
||||
let res = try_add_gateway(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
other_sender,
|
||||
gateway.clone(),
|
||||
signature.clone(),
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||
|
||||
// trying to reuse the same signature for another bonding fails (because nonce doesn't match!)
|
||||
let info = mock_info(sender, pledge.as_ref());
|
||||
let current_nonce =
|
||||
signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender))
|
||||
.unwrap();
|
||||
assert_eq!(0, current_nonce);
|
||||
let res = try_add_gateway(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
gateway.clone(),
|
||||
signature.clone(),
|
||||
);
|
||||
assert!(res.is_ok());
|
||||
let updated_nonce =
|
||||
signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender))
|
||||
.unwrap();
|
||||
assert_eq!(1, updated_nonce);
|
||||
|
||||
_try_remove_gateway(test.deps_mut(), Addr::unchecked(sender), None).unwrap();
|
||||
|
||||
let res = try_add_gateway(test.deps_mut(), env, info, gateway, signature);
|
||||
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_add_with_illegal_proxy() {
|
||||
let mut test = TestSetup::new();
|
||||
@@ -297,7 +356,7 @@ pub mod tests {
|
||||
let vesting_contract = test.vesting_contract();
|
||||
|
||||
let owner = "alice";
|
||||
let (gateway, sig) = test_helpers::gateway_with_signature(&mut test.rng, owner);
|
||||
let (gateway, sig) = test.gateway_with_signature(owner, None);
|
||||
|
||||
let res = try_add_gateway_on_behalf(
|
||||
test.deps_mut(),
|
||||
@@ -320,14 +379,13 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn gateway_remove() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let env = mock_env();
|
||||
let mut test = TestSetup::new();
|
||||
let env = test.env();
|
||||
|
||||
// try unbond when no nodes exist yet
|
||||
let info = mock_info("anyone", &[]);
|
||||
let msg = ExecuteMsg::UnbondGateway {};
|
||||
let result = execute(deps.as_mut(), mock_env(), info, msg);
|
||||
let result = execute(test.deps_mut(), env.clone(), info, msg);
|
||||
|
||||
// we're told that there is no node for our address
|
||||
assert_eq!(
|
||||
@@ -338,18 +396,12 @@ pub mod tests {
|
||||
);
|
||||
|
||||
// let's add a node owned by bob
|
||||
test_helpers::add_gateway(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
"bob",
|
||||
fixtures::good_gateway_pledge(),
|
||||
);
|
||||
test.add_dummy_gateway("bob", None);
|
||||
|
||||
// attempt to unbond fred's node, which doesn't exist
|
||||
let info = mock_info("fred", &[]);
|
||||
let msg = ExecuteMsg::UnbondGateway {};
|
||||
let result = execute(deps.as_mut(), mock_env(), info, msg);
|
||||
let result = execute(test.deps_mut(), env.clone(), info, msg);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(MixnetContractError::NoAssociatedGatewayBond {
|
||||
@@ -358,28 +410,27 @@ pub mod tests {
|
||||
);
|
||||
|
||||
// bob's node is still there
|
||||
let nodes = tests::queries::get_gateways(&mut deps);
|
||||
let nodes = queries::query_gateways_paged(test.deps(), None, None)
|
||||
.unwrap()
|
||||
.nodes;
|
||||
assert_eq!(1, nodes.len());
|
||||
|
||||
let first_node = &nodes[0];
|
||||
assert_eq!(&Addr::unchecked("bob"), first_node.owner());
|
||||
|
||||
// add a node owned by fred
|
||||
let fred_identity = test_helpers::add_gateway(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env,
|
||||
"fred",
|
||||
tests::fixtures::good_gateway_pledge(),
|
||||
);
|
||||
let fred_identity = test.add_dummy_gateway("fred", None);
|
||||
|
||||
// let's make sure we now have 2 nodes:
|
||||
assert_eq!(2, tests::queries::get_gateways(&mut deps).len());
|
||||
let nodes = queries::query_gateways_paged(test.deps(), None, None)
|
||||
.unwrap()
|
||||
.nodes;
|
||||
assert_eq!(2, nodes.len());
|
||||
|
||||
// unbond fred's node
|
||||
let info = mock_info("fred", &[]);
|
||||
let msg = ExecuteMsg::UnbondGateway {};
|
||||
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
|
||||
let remove_fred = execute(test.deps_mut(), env, info.clone(), msg).unwrap();
|
||||
|
||||
// we should see a funds transfer from the contract back to fred
|
||||
let expected_message = BankMsg::Send {
|
||||
@@ -401,9 +452,11 @@ pub mod tests {
|
||||
assert_eq!(expected_response, remove_fred);
|
||||
|
||||
// only 1 node now exists, owned by bob:
|
||||
let gateway_bonds = tests::queries::get_gateways(&mut deps);
|
||||
assert_eq!(1, gateway_bonds.len());
|
||||
assert_eq!(&Addr::unchecked("bob"), gateway_bonds[0].owner());
|
||||
let nodes = queries::query_gateways_paged(test.deps(), None, None)
|
||||
.unwrap()
|
||||
.nodes;
|
||||
assert_eq!(1, nodes.len());
|
||||
assert_eq!(&Addr::unchecked("bob"), nodes[0].owner());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -14,6 +14,7 @@ mod mixnet_contract_settings;
|
||||
mod mixnodes;
|
||||
mod queued_migrations;
|
||||
mod rewards;
|
||||
mod signing;
|
||||
mod support;
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
|
||||
pub mod helpers;
|
||||
pub mod queries;
|
||||
pub mod signature_helpers;
|
||||
pub mod storage;
|
||||
pub mod transactions;
|
||||
|
||||
@@ -273,30 +273,24 @@ pub(crate) mod tests {
|
||||
#[cfg(test)]
|
||||
mod mixnode_bonds {
|
||||
use super::*;
|
||||
use crate::support::tests::fixtures::good_mixnode_pledge;
|
||||
|
||||
#[test]
|
||||
fn obeys_limits() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_mixnodes(1000);
|
||||
let limit = 2;
|
||||
|
||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(limit)).unwrap();
|
||||
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(limit)).unwrap();
|
||||
assert_eq!(limit, page1.nodes.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_default_limit() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
|
||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_mixnodes(1000);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, None).unwrap();
|
||||
let page1 = query_mixnode_bonds_paged(test.deps(), None, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
MIXNODE_BOND_DEFAULT_RETRIEVAL_LIMIT,
|
||||
@@ -306,14 +300,12 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn has_max_limit() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_mixnodes(1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000;
|
||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(crazy_limit)).unwrap();
|
||||
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(crazy_limit)).unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
assert_eq!(MIXNODE_BOND_MAX_RETRIEVAL_LIMIT, page1.nodes.len() as u32);
|
||||
@@ -322,63 +314,43 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn pagination_works() {
|
||||
// as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
"addr1",
|
||||
good_mixnode_pledge(),
|
||||
);
|
||||
test.add_dummy_mixnode("addr1", None);
|
||||
|
||||
let per_page = 2;
|
||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
||||
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.nodes.len());
|
||||
|
||||
// save another
|
||||
test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
"addr2",
|
||||
good_mixnode_pledge(),
|
||||
);
|
||||
test.add_dummy_mixnode("addr2", None);
|
||||
|
||||
// page1 should have 2 results on it
|
||||
let page1 = query_mixnode_bonds_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
||||
let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||
assert_eq!(2, page1.nodes.len());
|
||||
|
||||
test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
"addr3",
|
||||
good_mixnode_pledge(),
|
||||
);
|
||||
test.add_dummy_mixnode("addr3", None);
|
||||
|
||||
// page1 still has the same 2 results
|
||||
let another_page1 =
|
||||
query_mixnode_bonds_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
||||
query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||
assert_eq!(2, another_page1.nodes.len());
|
||||
assert_eq!(page1, another_page1);
|
||||
|
||||
// retrieving the next page should start after the last key on this page
|
||||
let start_after = page1.start_next_after.unwrap();
|
||||
let page2 = query_mixnode_bonds_paged(deps.as_ref(), Some(start_after), Some(per_page))
|
||||
.unwrap();
|
||||
let page2 =
|
||||
query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap();
|
||||
|
||||
assert_eq!(1, page2.nodes.len());
|
||||
|
||||
// save another one
|
||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "addr4", good_mixnode_pledge());
|
||||
test.add_dummy_mixnode("addr4", None);
|
||||
|
||||
let page2 = query_mixnode_bonds_paged(deps.as_ref(), Some(start_after), Some(per_page))
|
||||
.unwrap();
|
||||
let page2 =
|
||||
query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.nodes.len());
|
||||
@@ -388,29 +360,24 @@ pub(crate) mod tests {
|
||||
#[cfg(test)]
|
||||
mod mixnode_details {
|
||||
use super::*;
|
||||
use crate::support::tests::fixtures::good_mixnode_pledge;
|
||||
|
||||
#[test]
|
||||
fn obeys_limits() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_mixnodes(1000);
|
||||
let limit = 2;
|
||||
|
||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
||||
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(limit)).unwrap();
|
||||
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(limit)).unwrap();
|
||||
assert_eq!(limit, page1.nodes.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_default_limit() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_mixnodes(1000);
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, None).unwrap();
|
||||
let page1 = query_mixnodes_details_paged(test.deps(), None, None).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
MIXNODE_DETAILS_DEFAULT_RETRIEVAL_LIMIT,
|
||||
@@ -420,15 +387,12 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn has_max_limit() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
test_helpers::add_dummy_mixnodes(&mut rng, deps.as_mut(), env, 1000);
|
||||
let mut test = TestSetup::new();
|
||||
test.add_dummy_mixnodes(1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000;
|
||||
let page1 =
|
||||
query_mixnodes_details_paged(deps.as_ref(), None, Some(crazy_limit)).unwrap();
|
||||
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(crazy_limit)).unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
assert_eq!(
|
||||
@@ -440,64 +404,44 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn pagination_works() {
|
||||
// as we add mixnodes, we're always inserting them in ascending manner due to monotonically increasing id
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
"addr1",
|
||||
good_mixnode_pledge(),
|
||||
);
|
||||
test.add_dummy_mixnode("addr1", None);
|
||||
|
||||
let per_page = 2;
|
||||
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
||||
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.nodes.len());
|
||||
|
||||
// save another
|
||||
test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
"addr2",
|
||||
good_mixnode_pledge(),
|
||||
);
|
||||
test.add_dummy_mixnode("addr2", None);
|
||||
|
||||
// page1 should have 2 results on it
|
||||
let page1 = query_mixnodes_details_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
||||
let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||
assert_eq!(2, page1.nodes.len());
|
||||
|
||||
test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
"addr3",
|
||||
good_mixnode_pledge(),
|
||||
);
|
||||
test.add_dummy_mixnode("addr3", None);
|
||||
|
||||
// page1 still has the same 2 results
|
||||
let another_page1 =
|
||||
query_mixnodes_details_paged(deps.as_ref(), None, Some(per_page)).unwrap();
|
||||
query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
|
||||
assert_eq!(2, another_page1.nodes.len());
|
||||
assert_eq!(page1, another_page1);
|
||||
|
||||
// retrieving the next page should start after the last key on this page
|
||||
let start_after = page1.start_next_after.unwrap();
|
||||
let page2 =
|
||||
query_mixnodes_details_paged(deps.as_ref(), Some(start_after), Some(per_page))
|
||||
query_mixnodes_details_paged(test.deps(), Some(start_after), Some(per_page))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.nodes.len());
|
||||
|
||||
// save another one
|
||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "addr4", good_mixnode_pledge());
|
||||
test.add_dummy_mixnode("addr4", None);
|
||||
|
||||
let page2 =
|
||||
query_mixnodes_details_paged(deps.as_ref(), Some(start_after), Some(per_page))
|
||||
query_mixnodes_details_paged(test.deps(), Some(start_after), Some(per_page))
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
@@ -1135,26 +1079,18 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn query_for_owned_mixnode() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
let address = "mix-owner".to_string();
|
||||
|
||||
// when it doesnt exist
|
||||
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
|
||||
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
|
||||
assert!(res.mixnode_details.is_none());
|
||||
assert_eq!(address, res.address);
|
||||
|
||||
// when it [fully] exists
|
||||
let id = test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env,
|
||||
&address,
|
||||
good_mixnode_pledge(),
|
||||
);
|
||||
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
|
||||
let id = test.add_dummy_mixnode(&address, None);
|
||||
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
|
||||
let details = res.mixnode_details.unwrap();
|
||||
assert_eq!(address, details.bond_information.owner);
|
||||
assert_eq!(
|
||||
@@ -1171,30 +1107,27 @@ pub(crate) mod tests {
|
||||
rewarding_details.delegates = Decimal::raw(12345);
|
||||
rewarding_details.unique_delegations = 1;
|
||||
rewards_storage::MIXNODE_REWARDING
|
||||
.save(deps.as_mut().storage, id, &rewarding_details)
|
||||
.save(test.deps_mut().storage, id, &rewarding_details)
|
||||
.unwrap();
|
||||
|
||||
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, id).unwrap();
|
||||
let res = query_owned_mixnode(deps.as_ref(), address.clone()).unwrap();
|
||||
pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, id).unwrap();
|
||||
let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
|
||||
assert!(res.mixnode_details.is_none());
|
||||
assert_eq!(address, res.address);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_for_mixnode_details() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
// no node under this id
|
||||
let res = query_mixnode_details(deps.as_ref(), 42).unwrap();
|
||||
let res = query_mixnode_details(test.deps(), 42).unwrap();
|
||||
assert!(res.mixnode_details.is_none());
|
||||
assert_eq!(42, res.mix_id);
|
||||
|
||||
// it exists
|
||||
let mix_id =
|
||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
|
||||
let res = query_mixnode_details(deps.as_ref(), mix_id).unwrap();
|
||||
let mix_id = test.add_dummy_mixnode("foomp", None);
|
||||
let res = query_mixnode_details(test.deps(), mix_id).unwrap();
|
||||
let details = res.mixnode_details.unwrap();
|
||||
assert_eq!(mix_id, details.bond_information.mix_id);
|
||||
assert_eq!(
|
||||
@@ -1227,18 +1160,15 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn query_for_mixnode_rewarding_details() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
// no node under this id
|
||||
let res = query_mixnode_rewarding_details(deps.as_ref(), 42).unwrap();
|
||||
let res = query_mixnode_rewarding_details(test.deps(), 42).unwrap();
|
||||
assert!(res.rewarding_details.is_none());
|
||||
assert_eq!(42, res.mix_id);
|
||||
|
||||
let mix_id =
|
||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
|
||||
let res = query_mixnode_rewarding_details(deps.as_ref(), mix_id).unwrap();
|
||||
let mix_id = test.add_dummy_mixnode("foomp", None);
|
||||
let res = query_mixnode_rewarding_details(test.deps(), mix_id).unwrap();
|
||||
let details = res.rewarding_details.unwrap();
|
||||
assert_eq!(
|
||||
fixtures::mix_node_cost_params_fixture(),
|
||||
@@ -1249,80 +1179,74 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn query_for_unbonded_mixnode() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
let sender = "mix-owner";
|
||||
|
||||
// no node under this id
|
||||
let res = query_unbonded_mixnode(deps.as_ref(), 42).unwrap();
|
||||
let res = query_unbonded_mixnode(test.deps(), 42).unwrap();
|
||||
assert!(res.unbonded_info.is_none());
|
||||
assert_eq!(42, res.mix_id);
|
||||
|
||||
// add and unbond the mixnode
|
||||
let mix_id =
|
||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, sender, good_mixnode_pledge());
|
||||
pending_events::unbond_mixnode(deps.as_mut(), &mock_env(), 123, mix_id).unwrap();
|
||||
let mix_id = test.add_dummy_mixnode(sender, None);
|
||||
pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, mix_id).unwrap();
|
||||
|
||||
let res = query_unbonded_mixnode(deps.as_ref(), mix_id).unwrap();
|
||||
let res = query_unbonded_mixnode(test.deps(), mix_id).unwrap();
|
||||
assert_eq!(res.unbonded_info.unwrap().owner, sender);
|
||||
assert_eq!(mix_id, res.mix_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_for_stake_saturation() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
|
||||
// no node under this id
|
||||
let res = query_stake_saturation(deps.as_ref(), 42).unwrap();
|
||||
let res = query_stake_saturation(test.deps(), 42).unwrap();
|
||||
assert!(res.current_saturation.is_none());
|
||||
assert!(res.uncapped_saturation.is_none());
|
||||
assert_eq!(42, res.mix_id);
|
||||
|
||||
let rewarding_params = rewards_storage::REWARDING_PARAMS
|
||||
.load(deps.as_ref().storage)
|
||||
.load(test.deps().storage)
|
||||
.unwrap();
|
||||
let saturation_point = rewarding_params.interval.stake_saturation_point;
|
||||
|
||||
let mix_id =
|
||||
test_helpers::add_mixnode(&mut rng, deps.as_mut(), env, "foomp", good_mixnode_pledge());
|
||||
let mix_id = test.add_dummy_mixnode("foomp", None);
|
||||
|
||||
// below saturation point
|
||||
// there's only the base pledge without any delegation
|
||||
let expected =
|
||||
Decimal::from_atomics(good_mixnode_pledge()[0].amount, 0).unwrap() / saturation_point;
|
||||
let res = query_stake_saturation(deps.as_ref(), mix_id).unwrap();
|
||||
let res = query_stake_saturation(test.deps(), mix_id).unwrap();
|
||||
assert_eq!(expected, res.current_saturation.unwrap());
|
||||
assert_eq!(expected, res.uncapped_saturation.unwrap());
|
||||
assert_eq!(mix_id, res.mix_id);
|
||||
|
||||
// exactly at saturation point
|
||||
let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
||||
.load(deps.as_ref().storage, mix_id)
|
||||
.load(test.deps().storage, mix_id)
|
||||
.unwrap();
|
||||
mix_rewarding.operator = saturation_point;
|
||||
rewards_storage::MIXNODE_REWARDING
|
||||
.save(deps.as_mut().storage, mix_id, &mix_rewarding)
|
||||
.save(test.deps_mut().storage, mix_id, &mix_rewarding)
|
||||
.unwrap();
|
||||
|
||||
let res = query_stake_saturation(deps.as_ref(), mix_id).unwrap();
|
||||
let res = query_stake_saturation(test.deps(), mix_id).unwrap();
|
||||
assert_eq!(Decimal::one(), res.current_saturation.unwrap());
|
||||
assert_eq!(Decimal::one(), res.uncapped_saturation.unwrap());
|
||||
assert_eq!(mix_id, res.mix_id);
|
||||
|
||||
// above the saturation point
|
||||
let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
||||
.load(deps.as_ref().storage, mix_id)
|
||||
.load(test.deps().storage, mix_id)
|
||||
.unwrap();
|
||||
mix_rewarding.delegates = mix_rewarding.operator * Decimal::percent(150);
|
||||
rewards_storage::MIXNODE_REWARDING
|
||||
.save(deps.as_mut().storage, mix_id, &mix_rewarding)
|
||||
.save(test.deps_mut().storage, mix_id, &mix_rewarding)
|
||||
.unwrap();
|
||||
|
||||
let res = query_stake_saturation(deps.as_ref(), mix_id).unwrap();
|
||||
let res = query_stake_saturation(test.deps(), mix_id).unwrap();
|
||||
assert_eq!(Decimal::one(), res.current_saturation.unwrap());
|
||||
assert_eq!(Decimal::percent(250), res.uncapped_saturation.unwrap());
|
||||
assert_eq!(mix_id, res.mix_id);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::signing::storage as signing_storage;
|
||||
use cosmwasm_std::{Addr, Coin, Deps};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
use mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode, MixNodeCostParams};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_contracts_common::signing::Verifier;
|
||||
|
||||
pub(crate) fn verify_mixnode_bonding_signature(
|
||||
deps: Deps<'_>,
|
||||
sender: Addr,
|
||||
proxy: Option<Addr>,
|
||||
pledge: Coin,
|
||||
mixnode: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
signature: MessageSignature,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
// recover the public key
|
||||
let mut public_key = [0u8; 32];
|
||||
bs58::decode(&mixnode.identity_key)
|
||||
.into(&mut public_key)
|
||||
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
|
||||
|
||||
// reconstruct the payload
|
||||
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
|
||||
let msg =
|
||||
construct_mixnode_bonding_sign_payload(nonce, sender, proxy, pledge, mixnode, cost_params);
|
||||
|
||||
if deps.api.verify_message(msg, signature, &public_key)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MixnetContractError::InvalidEd25519Signature)
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,11 @@ use crate::mixnet_contract_settings::storage::rewarding_denom;
|
||||
use crate::mixnodes::helpers::{
|
||||
get_mixnode_details_by_owner, must_get_mixnode_bond_by_owner, save_new_mixnode,
|
||||
};
|
||||
use crate::mixnodes::signature_helpers::verify_mixnode_bonding_signature;
|
||||
use crate::signing::storage as signing_storage;
|
||||
use crate::support::helpers::{
|
||||
ensure_bonded, ensure_epoch_in_progress_state, ensure_is_authorized, ensure_no_existing_bond,
|
||||
ensure_proxy_match, ensure_sent_by_vesting_contract, validate_node_identity_signature,
|
||||
validate_pledge,
|
||||
ensure_proxy_match, ensure_sent_by_vesting_contract, validate_pledge,
|
||||
};
|
||||
use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Storage};
|
||||
use mixnet_contract_common::error::MixnetContractError;
|
||||
@@ -24,6 +25,7 @@ use mixnet_contract_common::events::{
|
||||
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||
use mixnet_contract_common::pending_events::{PendingEpochEventKind, PendingIntervalEventKind};
|
||||
use mixnet_contract_common::{Layer, MixId, MixNode};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
|
||||
pub(crate) fn update_mixnode_layer(
|
||||
mix_id: MixId,
|
||||
@@ -61,7 +63,7 @@ pub fn try_add_mixnode(
|
||||
info: MessageInfo,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
_try_add_mixnode(
|
||||
deps,
|
||||
@@ -82,7 +84,7 @@ pub fn try_add_mixnode_on_behalf(
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner: String,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
ensure_sent_by_vesting_contract(&info, deps.storage)?;
|
||||
|
||||
@@ -101,6 +103,9 @@ pub fn try_add_mixnode_on_behalf(
|
||||
}
|
||||
|
||||
// I'm not entirely sure how to deal with this warning at the current moment
|
||||
//
|
||||
// TODO: perhaps also require the user to explicitly provide what it thinks is the current nonce
|
||||
// so that we could return a better error message if it doesn't match?
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn _try_add_mixnode(
|
||||
deps: DepsMut<'_>,
|
||||
@@ -109,7 +114,7 @@ fn _try_add_mixnode(
|
||||
cost_params: MixNodeCostParams,
|
||||
pledge: Vec<Coin>,
|
||||
owner: Addr,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Response, MixnetContractError> {
|
||||
// check if the pledge contains any funds of the appropriate denomination
|
||||
@@ -126,13 +131,19 @@ fn _try_add_mixnode(
|
||||
// the bond information due to `UniqueIndex` constraint defined on those fields.
|
||||
|
||||
// check if this sender actually owns the mixnode by checking the signature
|
||||
validate_node_identity_signature(
|
||||
verify_mixnode_bonding_signature(
|
||||
deps.as_ref(),
|
||||
&owner,
|
||||
&owner_signature,
|
||||
&mixnode.identity_key,
|
||||
owner.clone(),
|
||||
proxy.clone(),
|
||||
pledge.clone(),
|
||||
mixnode.clone(),
|
||||
cost_params.clone(),
|
||||
owner_signature,
|
||||
)?;
|
||||
|
||||
// update the signing nonce associated with this sender so that the future signature would be made on the new value
|
||||
signing_storage::increment_signing_nonce(deps.storage, owner.clone())?;
|
||||
|
||||
let node_identity = mixnode.identity_key.clone();
|
||||
let (node_id, layer) = save_new_mixnode(
|
||||
deps.storage,
|
||||
@@ -393,7 +404,7 @@ pub mod tests {
|
||||
use crate::support::tests::fixtures::{good_mixnode_pledge, TEST_COIN_DENOM};
|
||||
use crate::support::tests::test_helpers::TestSetup;
|
||||
use crate::support::tests::{fixtures, test_helpers};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::{Order, StdResult, Uint128};
|
||||
use mixnet_contract_common::{
|
||||
EpochState, EpochStatus, ExecuteMsg, Layer, LayerDistribution, Percent,
|
||||
@@ -401,23 +412,23 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn mixnode_add() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
let env = test.env();
|
||||
|
||||
let sender = "alice";
|
||||
let minimum_pledge = minimum_mixnode_pledge(deps.as_ref().storage).unwrap();
|
||||
let minimum_pledge = minimum_mixnode_pledge(test.deps().storage).unwrap();
|
||||
let mut insufficient_pledge = minimum_pledge.clone();
|
||||
insufficient_pledge.amount -= Uint128::new(1000);
|
||||
|
||||
// if we don't send enough funds
|
||||
let info = mock_info(sender, &[insufficient_pledge.clone()]);
|
||||
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut rng, sender);
|
||||
let (mixnode, sig, _) =
|
||||
test.mixnode_with_signature(sender, Some(vec![insufficient_pledge.clone()]));
|
||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||
|
||||
// we are informed that we didn't send enough funds
|
||||
let result = try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info,
|
||||
mixnode.clone(),
|
||||
@@ -435,31 +446,12 @@ pub mod tests {
|
||||
// if the signature provided is invalid, the bonding also fails
|
||||
let info = mock_info(sender, &[minimum_pledge]);
|
||||
|
||||
let result = try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
mixnode.clone(),
|
||||
cost_params.clone(),
|
||||
"bad-signature".into(),
|
||||
);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(MixnetContractError::MalformedEd25519Signature(..))
|
||||
));
|
||||
|
||||
// if there was already a mixnode bonded by particular user
|
||||
test_helpers::add_mixnode(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
sender,
|
||||
fixtures::good_mixnode_pledge(),
|
||||
);
|
||||
test.add_dummy_mixnode(sender, None);
|
||||
|
||||
// it fails
|
||||
let result = try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info,
|
||||
mixnode,
|
||||
@@ -471,19 +463,13 @@ pub mod tests {
|
||||
// the same holds if the user already owns a gateway
|
||||
let sender2 = "gateway-owner";
|
||||
|
||||
test_helpers::add_gateway(
|
||||
&mut rng,
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
sender2,
|
||||
tests::fixtures::good_gateway_pledge(),
|
||||
);
|
||||
test.add_dummy_gateway(sender2, None);
|
||||
|
||||
let info = mock_info(sender2, &tests::fixtures::good_mixnode_pledge());
|
||||
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut rng, sender2);
|
||||
let (mixnode, sig, _) = test.mixnode_with_signature(sender2, None);
|
||||
|
||||
let result = try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
mixnode.clone(),
|
||||
@@ -494,14 +480,14 @@ pub mod tests {
|
||||
|
||||
// but after he unbonds it, it's all fine again
|
||||
let msg = ExecuteMsg::UnbondGateway {};
|
||||
execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
|
||||
execute(test.deps_mut(), env.clone(), info.clone(), msg).unwrap();
|
||||
|
||||
let result = try_add_mixnode(deps.as_mut(), env, info, mixnode, cost_params, sig);
|
||||
let result = try_add_mixnode(test.deps_mut(), env, info, mixnode, cost_params, sig);
|
||||
assert!(result.is_ok());
|
||||
|
||||
// make sure we got assigned the next id (note: we have already bonded a mixnode before in this test)
|
||||
let bond = must_get_mixnode_bond_by_owner(deps.as_ref().storage, &Addr::unchecked(sender2))
|
||||
.unwrap();
|
||||
let bond =
|
||||
must_get_mixnode_bond_by_owner(test.deps().storage, &Addr::unchecked(sender2)).unwrap();
|
||||
assert_eq!(2, bond.mix_id);
|
||||
|
||||
// and make sure we're on layer 2 (because it was the next empty one)
|
||||
@@ -513,10 +499,84 @@ pub mod tests {
|
||||
layer2: 1,
|
||||
layer3: 0,
|
||||
};
|
||||
assert_eq!(
|
||||
expected,
|
||||
storage::LAYERS.load(deps.as_ref().storage).unwrap()
|
||||
)
|
||||
assert_eq!(expected, storage::LAYERS.load(test.deps().storage).unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_mixnode_with_invalid_signatures() {
|
||||
let mut test = TestSetup::new();
|
||||
let env = test.env();
|
||||
|
||||
let sender = "alice";
|
||||
let pledge = good_mixnode_pledge();
|
||||
let info = mock_info(sender, pledge.as_ref());
|
||||
|
||||
let (mixnode, signature, _) = test.mixnode_with_signature(sender, Some(pledge.clone()));
|
||||
// the above using cost params fixture
|
||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||
|
||||
// using different parameters than what the signature was made on
|
||||
let mut modified_mixnode = mixnode.clone();
|
||||
modified_mixnode.mix_port += 1;
|
||||
let res = try_add_mixnode(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info,
|
||||
modified_mixnode,
|
||||
cost_params.clone(),
|
||||
signature.clone(),
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||
|
||||
// even stake amount is protected
|
||||
let mut different_pledge = pledge.clone();
|
||||
different_pledge[0].amount += Uint128::new(12345);
|
||||
|
||||
let info = mock_info(sender, different_pledge.as_ref());
|
||||
let res = try_add_mixnode(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
mixnode.clone(),
|
||||
cost_params.clone(),
|
||||
signature.clone(),
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||
|
||||
let other_sender = mock_info("another-sender", pledge.as_ref());
|
||||
let res = try_add_mixnode(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
other_sender,
|
||||
mixnode.clone(),
|
||||
cost_params.clone(),
|
||||
signature.clone(),
|
||||
);
|
||||
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||
|
||||
// trying to reuse the same signature for another bonding fails (because nonce doesn't match!)
|
||||
let info = mock_info(sender, pledge.as_ref());
|
||||
let current_nonce =
|
||||
signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender))
|
||||
.unwrap();
|
||||
assert_eq!(0, current_nonce);
|
||||
let res = try_add_mixnode(
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
mixnode.clone(),
|
||||
cost_params.clone(),
|
||||
signature.clone(),
|
||||
);
|
||||
assert!(res.is_ok());
|
||||
let updated_nonce =
|
||||
signing_storage::get_signing_nonce(test.deps().storage, Addr::unchecked(sender))
|
||||
.unwrap();
|
||||
assert_eq!(1, updated_nonce);
|
||||
|
||||
test.immediately_unbond_mixnode(1);
|
||||
let res = try_add_mixnode(test.deps_mut(), env, info, mixnode, cost_params, signature);
|
||||
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -528,10 +588,9 @@ pub mod tests {
|
||||
let vesting_contract = test.vesting_contract();
|
||||
|
||||
let owner = "alice";
|
||||
let (mixnode, sig, _) = test_helpers::mixnode_with_signature(&mut test.rng, owner);
|
||||
let (mixnode, sig, _) = test.mixnode_with_signature(owner, None);
|
||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||
|
||||
// we are informed that we didn't send enough funds
|
||||
let res = try_add_mixnode_on_behalf(
|
||||
test.deps_mut(),
|
||||
env,
|
||||
@@ -920,52 +979,53 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn adding_mixnode_with_duplicate_sphinx_key_errors_out() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let mut rng = test_helpers::test_rng();
|
||||
let mut test = TestSetup::new();
|
||||
let env = test.env();
|
||||
|
||||
let keypair1 = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
||||
let keypair2 = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
||||
let sig1 = keypair1.private_key().sign_text("alice");
|
||||
let sig2 = keypair1.private_key().sign_text("bob");
|
||||
let keypair1 = nym_crypto::asymmetric::identity::KeyPair::new(&mut test.rng);
|
||||
let keypair2 = nym_crypto::asymmetric::identity::KeyPair::new(&mut test.rng);
|
||||
|
||||
let info_alice = mock_info("alice", &tests::fixtures::good_mixnode_pledge());
|
||||
let info_bob = mock_info("bob", &tests::fixtures::good_mixnode_pledge());
|
||||
|
||||
let mut mixnode = MixNode {
|
||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||
let mixnode1 = MixNode {
|
||||
host: "1.2.3.4".to_string(),
|
||||
mix_port: 1234,
|
||||
verloc_port: 1234,
|
||||
http_api_port: 1234,
|
||||
sphinx_key: nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng)
|
||||
sphinx_key: nym_crypto::asymmetric::encryption::KeyPair::new(&mut test.rng)
|
||||
.public_key()
|
||||
.to_base58_string(),
|
||||
identity_key: keypair1.public_key().to_base58_string(),
|
||||
version: "v0.1.2.3".to_string(),
|
||||
};
|
||||
let cost_params = fixtures::mix_node_cost_params_fixture();
|
||||
|
||||
// change identity but reuse sphinx key
|
||||
let mut mixnode2 = mixnode1.clone();
|
||||
mixnode2.sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut test.rng)
|
||||
.public_key()
|
||||
.to_base58_string();
|
||||
|
||||
let sig1 =
|
||||
test.mixnode_bonding_signature(keypair1.private_key(), "alice", mixnode1.clone(), None);
|
||||
let sig2 =
|
||||
test.mixnode_bonding_signature(keypair2.private_key(), "bob", mixnode2.clone(), None);
|
||||
|
||||
let info_alice = mock_info("alice", &tests::fixtures::good_mixnode_pledge());
|
||||
let info_bob = mock_info("bob", &tests::fixtures::good_mixnode_pledge());
|
||||
|
||||
assert!(try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
test.deps_mut(),
|
||||
env.clone(),
|
||||
info_alice,
|
||||
mixnode.clone(),
|
||||
mixnode1,
|
||||
cost_params.clone(),
|
||||
sig1
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
mixnode.identity_key = keypair2.public_key().to_base58_string();
|
||||
|
||||
// change identity but reuse sphinx key
|
||||
assert!(try_add_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
info_bob,
|
||||
mixnode,
|
||||
cost_params,
|
||||
sig2
|
||||
)
|
||||
.is_err());
|
||||
assert!(
|
||||
try_add_mixnode(test.deps_mut(), env, info_bob, mixnode2, cost_params, sig2).is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod queries;
|
||||
pub mod storage;
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::signing::storage::get_signing_nonce;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use nym_contracts_common::signing::Nonce;
|
||||
|
||||
pub fn query_current_signing_nonce(deps: Deps<'_>, address: String) -> StdResult<Nonce> {
|
||||
let address = deps.api.addr_validate(&address)?;
|
||||
get_signing_nonce(deps.storage, address)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::constants::SIGNING_NONCES_NAMESPACE;
|
||||
use cosmwasm_std::{Addr, StdResult, Storage};
|
||||
use cw_storage_plus::Map;
|
||||
use nym_contracts_common::signing::Nonce;
|
||||
|
||||
pub const NONCES: Map<'_, Addr, Nonce> = Map::new(SIGNING_NONCES_NAMESPACE);
|
||||
|
||||
pub fn get_signing_nonce(storage: &dyn Storage, address: Addr) -> StdResult<Nonce> {
|
||||
let nonce = NONCES.may_load(storage, address)?.unwrap_or(0);
|
||||
Ok(nonce)
|
||||
}
|
||||
|
||||
pub fn update_signing_nonce(
|
||||
storage: &mut dyn Storage,
|
||||
address: Addr,
|
||||
value: Nonce,
|
||||
) -> StdResult<()> {
|
||||
NONCES.save(storage, address, &value)
|
||||
}
|
||||
|
||||
pub fn increment_signing_nonce(storage: &mut dyn Storage, address: Addr) -> StdResult<()> {
|
||||
// get the current nonce
|
||||
let nonce = get_signing_nonce(storage, address.clone())?;
|
||||
|
||||
// increment it for the next use
|
||||
update_signing_nonce(storage, address, nonce + 1)
|
||||
}
|
||||
@@ -377,7 +377,7 @@ pub fn validate_node_identity_signature(
|
||||
signature: &str,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
validate_signature(deps, owner.as_bytes(), signature, identity)
|
||||
validate_ed25519_signature(deps, owner.as_bytes(), signature, identity)
|
||||
}
|
||||
|
||||
pub fn validate_family_signature(
|
||||
@@ -386,10 +386,10 @@ pub fn validate_family_signature(
|
||||
signature: &str,
|
||||
family_head: IdentityKeyRef<'_>,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
validate_signature(deps, family_member.as_bytes(), signature, family_head)
|
||||
validate_ed25519_signature(deps, family_member.as_bytes(), signature, family_head)
|
||||
}
|
||||
|
||||
pub(crate) fn validate_signature(
|
||||
pub(crate) fn validate_ed25519_signature(
|
||||
deps: Deps<'_>,
|
||||
signed_bytes: &[u8],
|
||||
signature: &str,
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
use cosmwasm_std::{Coin, Deps};
|
||||
use mixnet_contract_common::{ExecuteMsg, Gateway, IdentityKey};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use rand_chacha::rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::support::tests;
|
||||
use crate::support::tests::test_helpers::{ed25519_sign_message, gateway_bonding_sign_payload};
|
||||
|
||||
pub(crate) fn valid_bond_gateway_msg(
|
||||
mut rng: impl RngCore + CryptoRng,
|
||||
deps: Deps<'_>,
|
||||
stake: Vec<Coin>,
|
||||
sender: &str,
|
||||
) -> (ExecuteMsg, IdentityKey) {
|
||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
||||
let owner_signature = keypair
|
||||
.private_key()
|
||||
.sign(sender.as_bytes())
|
||||
.to_base58_string();
|
||||
let keypair = identity::KeyPair::new(&mut rng);
|
||||
let identity_key = keypair.public_key().to_base58_string();
|
||||
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||
|
||||
let gateway = Gateway {
|
||||
identity_key,
|
||||
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||
..tests::fixtures::gateway_fixture()
|
||||
};
|
||||
|
||||
let msg = gateway_bonding_sign_payload(deps, sender, None, gateway.clone(), stake);
|
||||
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||
|
||||
let identity_key = keypair.public_key().to_base58_string();
|
||||
(
|
||||
ExecuteMsg::BondGateway {
|
||||
gateway: Gateway {
|
||||
identity_key: identity_key.clone(),
|
||||
..tests::fixtures::gateway_fixture()
|
||||
},
|
||||
gateway,
|
||||
owner_signature,
|
||||
},
|
||||
identity_key,
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
pub mod fixtures;
|
||||
#[cfg(test)]
|
||||
pub mod messages;
|
||||
#[cfg(test)]
|
||||
pub mod queries;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_helpers {
|
||||
@@ -24,7 +22,8 @@ pub mod test_helpers {
|
||||
use crate::interval::{pending_events, storage as interval_storage};
|
||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use crate::mixnet_contract_settings::storage::{
|
||||
minimum_mixnode_pledge, rewarding_denom, rewarding_validator_address,
|
||||
minimum_gateway_pledge, minimum_mixnode_pledge, rewarding_denom,
|
||||
rewarding_validator_address,
|
||||
};
|
||||
use crate::mixnodes::storage as mixnodes_storage;
|
||||
use crate::mixnodes::storage::mixnode_bonds;
|
||||
@@ -36,8 +35,11 @@ pub mod test_helpers {
|
||||
};
|
||||
use crate::rewards::storage as rewards_storage;
|
||||
use crate::rewards::transactions::try_reward_mixnode;
|
||||
use crate::signing::storage as signing_storage;
|
||||
use crate::support::tests;
|
||||
use crate::support::tests::fixtures::TEST_COIN_DENOM;
|
||||
use crate::support::tests::fixtures::{
|
||||
good_gateway_pledge, good_mixnode_pledge, TEST_COIN_DENOM,
|
||||
};
|
||||
use cosmwasm_std::testing::mock_dependencies;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
@@ -59,13 +61,19 @@ pub mod test_helpers {
|
||||
use mixnet_contract_common::rewarding::simulator::Simulator;
|
||||
use mixnet_contract_common::rewarding::RewardDistribution;
|
||||
use mixnet_contract_common::{
|
||||
Delegation, EpochState, EpochStatus, Gateway, IdentityKey, InitialRewardingParams,
|
||||
InstantiateMsg, Interval, MixId, MixNode, MixNodeBond, Percent, RewardedSetNodeStatus,
|
||||
Delegation, EpochState, EpochStatus, Gateway, GatewayBondingPayload, IdentityKey,
|
||||
InitialRewardingParams, InstantiateMsg, Interval, MixId, MixNode, MixNodeBond,
|
||||
MixnodeBondingPayload, Percent, RewardedSetNodeStatus, SignableGatewayBondingMsg,
|
||||
SignableMixNodeBondingMsg,
|
||||
};
|
||||
use nym_contracts_common::signing::{
|
||||
ContractMessageContent, MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose,
|
||||
};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_crypto::asymmetric::identity::KeyPair;
|
||||
use rand_chacha::rand_core::{CryptoRng, RngCore, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use serde::Serialize;
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn assert_eq_with_leeway(a: Uint128, b: Uint128, leeway: Uint128) {
|
||||
@@ -190,7 +198,7 @@ pub mod test_helpers {
|
||||
head: &str,
|
||||
label: &str,
|
||||
) -> (MixId, identity::KeyPair) {
|
||||
let (mix_id, keys) = self.add_dummy_mixnode_with_keypair(head, None);
|
||||
let (mix_id, keys) = self.add_dummy_mixnode_with_proxy_and_keypair(head, None);
|
||||
let sig = keys.private_key().sign_text(head);
|
||||
|
||||
try_create_family(self.deps_mut(), mock_info(head, &[]), sig, label).unwrap();
|
||||
@@ -198,6 +206,57 @@ pub mod test_helpers {
|
||||
}
|
||||
|
||||
pub fn add_dummy_mixnode(&mut self, owner: &str, stake: Option<Uint128>) -> MixId {
|
||||
let stake = self.make_mix_pledge(stake);
|
||||
let (mixnode, owner_signature, _) =
|
||||
self.mixnode_with_signature(owner, Some(stake.clone()));
|
||||
|
||||
let info = mock_info(owner, stake.as_ref());
|
||||
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
|
||||
.may_load(self.deps().storage)
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
|
||||
let env = self.env();
|
||||
|
||||
try_add_mixnode(
|
||||
self.deps_mut(),
|
||||
env,
|
||||
info,
|
||||
mixnode,
|
||||
tests::fixtures::mix_node_cost_params_fixture(),
|
||||
owner_signature,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// newly added mixnode gets assigned the current counter + 1
|
||||
current_id_counter + 1
|
||||
}
|
||||
|
||||
pub fn add_dummy_gateway(&mut self, sender: &str, stake: Option<Uint128>) -> IdentityKey {
|
||||
let stake = self.make_gateway_pledge(stake);
|
||||
let (gateway, owner_signature) =
|
||||
self.gateway_with_signature(sender, Some(stake.clone()));
|
||||
|
||||
let info = mock_info(sender, &stake);
|
||||
let key = gateway.identity_key.clone();
|
||||
let env = self.env();
|
||||
try_add_gateway(self.deps_mut(), env, info, gateway, owner_signature).unwrap();
|
||||
key
|
||||
}
|
||||
|
||||
pub fn add_dummy_mixnodes(&mut self, n: usize) {
|
||||
for i in 0..n {
|
||||
self.add_dummy_mixnode(&format!("owner{i}"), None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_dummy_gateways(&mut self, n: usize) {
|
||||
for i in 0..n {
|
||||
self.add_dummy_gateway(&format!("owner{i}"), None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_mix_pledge(&self, stake: Option<Uint128>) -> Vec<Coin> {
|
||||
let stake = match stake {
|
||||
Some(amount) => {
|
||||
let denom = rewarding_denom(self.deps().storage).unwrap();
|
||||
@@ -205,36 +264,61 @@ pub mod test_helpers {
|
||||
}
|
||||
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
|
||||
};
|
||||
|
||||
let env = self.env();
|
||||
add_mixnode(&mut self.rng, self.deps.as_mut(), env, owner, vec![stake])
|
||||
vec![stake]
|
||||
}
|
||||
|
||||
pub fn add_dummy_mixnode_with_keypair(
|
||||
pub fn make_gateway_pledge(&self, stake: Option<Uint128>) -> Vec<Coin> {
|
||||
let stake = match stake {
|
||||
Some(amount) => {
|
||||
let denom = rewarding_denom(self.deps().storage).unwrap();
|
||||
Coin { denom, amount }
|
||||
}
|
||||
None => minimum_gateway_pledge(self.deps.as_ref().storage).unwrap(),
|
||||
};
|
||||
vec![stake]
|
||||
}
|
||||
|
||||
pub fn mixnode_bonding_signature(
|
||||
&mut self,
|
||||
key: &identity::PrivateKey,
|
||||
owner: &str,
|
||||
mixnode: MixNode,
|
||||
stake: Option<Uint128>,
|
||||
) -> MessageSignature {
|
||||
let stake = self.make_mix_pledge(stake);
|
||||
let msg = mixnode_bonding_sign_payload(self.deps(), owner, None, mixnode, stake);
|
||||
ed25519_sign_message(msg, key)
|
||||
}
|
||||
|
||||
pub fn add_dummy_mixnode_with_proxy_and_keypair(
|
||||
&mut self,
|
||||
owner: &str,
|
||||
stake: Option<Uint128>,
|
||||
) -> (MixId, identity::KeyPair) {
|
||||
let stake = match stake {
|
||||
Some(amount) => {
|
||||
let denom = rewarding_denom(self.deps().storage).unwrap();
|
||||
Coin { denom, amount }
|
||||
}
|
||||
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
|
||||
};
|
||||
let stake = self.make_mix_pledge(stake);
|
||||
|
||||
let proxy = self.vesting_contract();
|
||||
|
||||
let keypair = identity::KeyPair::new(&mut self.rng);
|
||||
let owner_signature = keypair
|
||||
.private_key()
|
||||
.sign(owner.as_bytes())
|
||||
.to_base58_string();
|
||||
let identity_key = keypair.public_key().to_base58_string();
|
||||
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||
|
||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||
let mixnode = MixNode {
|
||||
identity_key,
|
||||
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||
..tests::fixtures::mix_node_fixture()
|
||||
};
|
||||
|
||||
let info = mock_info(proxy.as_str(), &[stake]);
|
||||
let key = keypair.public_key().to_base58_string();
|
||||
let msg = mixnode_bonding_sign_payload(
|
||||
self.deps(),
|
||||
owner,
|
||||
Some(proxy.clone()),
|
||||
mixnode.clone(),
|
||||
stake.clone(),
|
||||
);
|
||||
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||
|
||||
let info = mock_info(proxy.as_str(), &stake);
|
||||
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
|
||||
.may_load(self.deps().storage)
|
||||
.unwrap()
|
||||
@@ -245,11 +329,7 @@ pub mod test_helpers {
|
||||
self.deps_mut(),
|
||||
env,
|
||||
info,
|
||||
MixNode {
|
||||
identity_key: key,
|
||||
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
|
||||
..tests::fixtures::mix_node_fixture()
|
||||
},
|
||||
mixnode,
|
||||
tests::fixtures::mix_node_cost_params_fixture(),
|
||||
owner.to_string(),
|
||||
owner_signature,
|
||||
@@ -265,7 +345,8 @@ pub mod test_helpers {
|
||||
owner: &str,
|
||||
stake: Option<Uint128>,
|
||||
) -> MixId {
|
||||
self.add_dummy_mixnode_with_keypair(owner, stake).0
|
||||
self.add_dummy_mixnode_with_proxy_and_keypair(owner, stake)
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn set_illegal_mixnode_proxy(&mut self, mix_id: MixId, proxy: Addr) {
|
||||
@@ -312,14 +393,26 @@ pub mod test_helpers {
|
||||
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
|
||||
};
|
||||
|
||||
let keypair = identity::KeyPair::new(&mut self.rng);
|
||||
let identity_key = keypair.public_key().to_base58_string();
|
||||
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||
|
||||
let proxy = self.vesting_contract();
|
||||
|
||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut self.rng);
|
||||
let owner_signature = keypair
|
||||
.private_key()
|
||||
.sign(owner.as_bytes())
|
||||
.to_base58_string();
|
||||
let gateway = Gateway {
|
||||
identity_key,
|
||||
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||
..tests::fixtures::gateway_fixture()
|
||||
};
|
||||
|
||||
let msg = gateway_bonding_sign_payload(
|
||||
self.deps(),
|
||||
owner,
|
||||
Some(proxy.clone()),
|
||||
gateway.clone(),
|
||||
vec![stake.clone()],
|
||||
);
|
||||
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||
|
||||
let env = self.env();
|
||||
let info = mock_info(proxy.as_ref(), &[stake]);
|
||||
@@ -328,11 +421,7 @@ pub mod test_helpers {
|
||||
self.deps_mut(),
|
||||
env,
|
||||
info,
|
||||
Gateway {
|
||||
identity_key: keypair.public_key().to_base58_string(),
|
||||
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
|
||||
..tests::fixtures::gateway_fixture()
|
||||
},
|
||||
gateway,
|
||||
owner.to_string(),
|
||||
owner_signature,
|
||||
)
|
||||
@@ -351,6 +440,63 @@ pub mod test_helpers {
|
||||
mix_id
|
||||
}
|
||||
|
||||
pub fn mixnode_with_signature(
|
||||
&mut self,
|
||||
sender: &str,
|
||||
stake: Option<Vec<Coin>>,
|
||||
) -> (MixNode, MessageSignature, KeyPair) {
|
||||
let stake = stake.unwrap_or(good_mixnode_pledge());
|
||||
|
||||
let keypair = identity::KeyPair::new(&mut self.rng);
|
||||
let identity_key = keypair.public_key().to_base58_string();
|
||||
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||
|
||||
let mixnode = MixNode {
|
||||
identity_key,
|
||||
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||
..tests::fixtures::mix_node_fixture()
|
||||
};
|
||||
let msg = mixnode_bonding_sign_payload(
|
||||
self.deps(),
|
||||
sender,
|
||||
None,
|
||||
mixnode.clone(),
|
||||
stake.clone(),
|
||||
);
|
||||
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||
|
||||
(mixnode, owner_signature, keypair)
|
||||
}
|
||||
|
||||
pub fn gateway_with_signature(
|
||||
&mut self,
|
||||
sender: &str,
|
||||
stake: Option<Vec<Coin>>,
|
||||
) -> (Gateway, MessageSignature) {
|
||||
let stake = stake.unwrap_or(good_gateway_pledge());
|
||||
|
||||
let keypair = identity::KeyPair::new(&mut self.rng);
|
||||
let identity_key = keypair.public_key().to_base58_string();
|
||||
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
|
||||
|
||||
let gateway = Gateway {
|
||||
identity_key,
|
||||
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||
..tests::fixtures::gateway_fixture()
|
||||
};
|
||||
|
||||
let msg = gateway_bonding_sign_payload(
|
||||
self.deps(),
|
||||
sender,
|
||||
None,
|
||||
gateway.clone(),
|
||||
stake.clone(),
|
||||
);
|
||||
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||
|
||||
(gateway, owner_signature)
|
||||
}
|
||||
|
||||
pub fn start_unbonding_mixnode(&mut self, mix_id: MixId) {
|
||||
let bond_details = mixnodes_storage::mixnode_bonds()
|
||||
.load(self.deps().storage, mix_id)
|
||||
@@ -714,6 +860,22 @@ pub mod test_helpers {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ed25519_sign_message<T: Serialize + SigningPurpose>(
|
||||
message: SignableMessage<T>,
|
||||
private_key: &identity::PrivateKey,
|
||||
) -> MessageSignature {
|
||||
match message.algorithm {
|
||||
SigningAlgorithm::Ed25519 => {
|
||||
let plaintext = message.to_plaintext().unwrap();
|
||||
let signature = private_key.sign(&plaintext);
|
||||
MessageSignature::from(signature.to_bytes().as_ref())
|
||||
}
|
||||
SigningAlgorithm::Secp256k1 => {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulator_from_single_node_state(deps: Deps<'_>, node: MixId) -> Simulator {
|
||||
let mix_rewarding = rewards_storage::MIXNODE_REWARDING
|
||||
.load(deps.storage, node)
|
||||
@@ -800,54 +962,54 @@ pub mod test_helpers {
|
||||
perform_pending_interval_actions(deps.branch(), &env, None).unwrap();
|
||||
}
|
||||
|
||||
pub fn mixnode_with_signature(
|
||||
mut rng: impl RngCore + CryptoRng,
|
||||
sender: &str,
|
||||
) -> (MixNode, String, KeyPair) {
|
||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||
let owner_signature = keypair
|
||||
.private_key()
|
||||
.sign(sender.as_bytes())
|
||||
.to_base58_string();
|
||||
// pub fn mixnode_with_signature(
|
||||
// mut rng: impl RngCore + CryptoRng,
|
||||
// deps: Deps<'_>,
|
||||
// sender: &str,
|
||||
// stake: Option<Vec<Coin>>,
|
||||
// ) -> (MixNode, MessageSignature, KeyPair) {
|
||||
// // hehe stupid workaround for bypassing the immutable borrow and removing duplicate code
|
||||
//
|
||||
// let stake = stake.unwrap_or(good_mixnode_pledge());
|
||||
//
|
||||
// let keypair = identity::KeyPair::new(&mut rng);
|
||||
// let identity_key = keypair.public_key().to_base58_string();
|
||||
// let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||
//
|
||||
// let mixnode = MixNode {
|
||||
// identity_key,
|
||||
// sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||
// ..tests::fixtures::mix_node_fixture()
|
||||
// };
|
||||
// let msg = mixnode_bonding_sign_payload(deps, sender, None, mixnode.clone(), stake.clone());
|
||||
// let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||
//
|
||||
// (mixnode, owner_signature, keypair)
|
||||
// }
|
||||
|
||||
let identity_key = keypair.public_key().to_base58_string();
|
||||
let sphinx_key = legit_sphinx_key.public_key().to_base58_string();
|
||||
|
||||
(
|
||||
MixNode {
|
||||
identity_key,
|
||||
sphinx_key,
|
||||
..tests::fixtures::mix_node_fixture()
|
||||
},
|
||||
owner_signature,
|
||||
keypair,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn gateway_with_signature(
|
||||
mut rng: impl RngCore + CryptoRng,
|
||||
sender: &str,
|
||||
) -> (Gateway, String) {
|
||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||
let owner_signature = keypair
|
||||
.private_key()
|
||||
.sign(sender.as_bytes())
|
||||
.to_base58_string();
|
||||
|
||||
let identity_key = keypair.public_key().to_base58_string();
|
||||
let sphinx_key = legit_sphinx_key.public_key().to_base58_string();
|
||||
|
||||
(
|
||||
Gateway {
|
||||
identity_key,
|
||||
sphinx_key,
|
||||
..tests::fixtures::gateway_fixture()
|
||||
},
|
||||
owner_signature,
|
||||
)
|
||||
}
|
||||
// pub fn gateway_with_signature(
|
||||
// mut rng: impl RngCore + CryptoRng,
|
||||
// deps: Deps<'_>,
|
||||
// sender: &str,
|
||||
// stake: Option<Vec<Coin>>,
|
||||
// ) -> (Gateway, MessageSignature) {
|
||||
// let stake = stake.unwrap_or(good_gateway_pledge());
|
||||
//
|
||||
// let keypair = identity::KeyPair::new(&mut rng);
|
||||
// let identity_key = keypair.public_key().to_base58_string();
|
||||
// let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||
//
|
||||
// let gateway = Gateway {
|
||||
// identity_key,
|
||||
// sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
|
||||
// ..tests::fixtures::gateway_fixture()
|
||||
// };
|
||||
//
|
||||
// let msg = gateway_bonding_sign_payload(deps, sender, None, gateway.clone(), stake.clone());
|
||||
// let owner_signature = ed25519_sign_message(msg, keypair.private_key());
|
||||
//
|
||||
// (gateway, owner_signature)
|
||||
// }
|
||||
|
||||
pub fn add_dummy_delegations(mut deps: DepsMut<'_>, env: Env, mix_id: MixId, n: usize) {
|
||||
for i in 0..n {
|
||||
@@ -864,39 +1026,22 @@ pub mod test_helpers {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_dummy_mixnodes(
|
||||
mut rng: impl RngCore + CryptoRng,
|
||||
mut deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
n: usize,
|
||||
) {
|
||||
for i in 0..n {
|
||||
add_mixnode(
|
||||
&mut rng,
|
||||
deps.branch(),
|
||||
env.clone(),
|
||||
&format!("owner{}", i),
|
||||
tests::fixtures::good_mixnode_pledge(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_dummy_gateways(
|
||||
mut rng: impl RngCore + CryptoRng,
|
||||
mut deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
n: usize,
|
||||
) {
|
||||
for i in 0..n {
|
||||
add_gateway(
|
||||
&mut rng,
|
||||
deps.branch(),
|
||||
env.clone(),
|
||||
&format!("owner{}", i),
|
||||
tests::fixtures::good_mixnode_pledge(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// pub fn add_dummy_mixnodes(
|
||||
// mut rng: impl RngCore + CryptoRng,
|
||||
// mut deps: DepsMut<'_>,
|
||||
// env: Env,
|
||||
// n: usize,
|
||||
// ) {
|
||||
// for i in 0..n {
|
||||
// add_mixnode(
|
||||
// &mut rng,
|
||||
// deps.branch(),
|
||||
// env.clone(),
|
||||
// &format!("owner{}", i),
|
||||
// tests::fixtures::good_mixnode_pledge(),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn add_dummy_unbonded_mixnodes(
|
||||
mut rng: impl RngCore + CryptoRng,
|
||||
@@ -968,79 +1113,35 @@ pub mod test_helpers {
|
||||
id
|
||||
}
|
||||
|
||||
// note to whoever wants to refactor this function, you dont want to grab rng here directly
|
||||
// via `let rng = test_rng()`
|
||||
// because it's extremely likely you might end up calling `add_mixnode()` multiple times
|
||||
// in the same test and thus you're going to get mixnodes with the same keys and that's
|
||||
// not what you want (presumably)
|
||||
pub fn add_mixnode(
|
||||
mut rng: impl RngCore + CryptoRng,
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
sender: &str,
|
||||
pub fn mixnode_bonding_sign_payload(
|
||||
deps: Deps<'_>,
|
||||
owner: &str,
|
||||
proxy: Option<Addr>,
|
||||
mixnode: MixNode,
|
||||
stake: Vec<Coin>,
|
||||
) -> MixId {
|
||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
||||
let owner_signature = keypair
|
||||
.private_key()
|
||||
.sign(sender.as_bytes())
|
||||
.to_base58_string();
|
||||
) -> SignableMixNodeBondingMsg {
|
||||
let cost_params = tests::fixtures::mix_node_cost_params_fixture();
|
||||
let nonce =
|
||||
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
|
||||
|
||||
let legit_sphinx_key = nym_crypto::asymmetric::encryption::KeyPair::new(&mut rng);
|
||||
|
||||
let info = mock_info(sender, &stake);
|
||||
let key = keypair.public_key().to_base58_string();
|
||||
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
|
||||
.may_load(deps.storage)
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
|
||||
try_add_mixnode(
|
||||
deps,
|
||||
env,
|
||||
info,
|
||||
MixNode {
|
||||
identity_key: key,
|
||||
sphinx_key: legit_sphinx_key.public_key().to_base58_string(),
|
||||
..tests::fixtures::mix_node_fixture()
|
||||
},
|
||||
tests::fixtures::mix_node_cost_params_fixture(),
|
||||
owner_signature,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// newly added mixnode gets assigned the current counter + 1
|
||||
current_id_counter + 1
|
||||
let payload = MixnodeBondingPayload::new(mixnode, cost_params);
|
||||
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
|
||||
SignableMixNodeBondingMsg::new(nonce, content)
|
||||
}
|
||||
|
||||
// same note as with `add_mixnode`
|
||||
pub fn add_gateway(
|
||||
mut rng: impl RngCore + CryptoRng,
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
sender: &str,
|
||||
pub fn gateway_bonding_sign_payload(
|
||||
deps: Deps<'_>,
|
||||
owner: &str,
|
||||
proxy: Option<Addr>,
|
||||
gateway: Gateway,
|
||||
stake: Vec<Coin>,
|
||||
) -> String {
|
||||
let keypair = nym_crypto::asymmetric::identity::KeyPair::new(&mut rng);
|
||||
let owner_signature = keypair
|
||||
.private_key()
|
||||
.sign(sender.as_bytes())
|
||||
.to_base58_string();
|
||||
) -> SignableGatewayBondingMsg {
|
||||
let nonce =
|
||||
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
|
||||
|
||||
let info = mock_info(sender, &stake);
|
||||
let key = keypair.public_key().to_base58_string();
|
||||
try_add_gateway(
|
||||
deps,
|
||||
env,
|
||||
info,
|
||||
Gateway {
|
||||
identity_key: key.clone(),
|
||||
..tests::fixtures::gateway_fixture()
|
||||
},
|
||||
owner_signature,
|
||||
)
|
||||
.unwrap();
|
||||
key
|
||||
let payload = GatewayBondingPayload::new(gateway);
|
||||
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
|
||||
SignableGatewayBondingMsg::new(nonce, content)
|
||||
}
|
||||
|
||||
fn initial_rewarding_params() -> InitialRewardingParams {
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
use crate::contract::query;
|
||||
use cosmwasm_std::{
|
||||
from_binary,
|
||||
testing::{mock_env, MockApi, MockQuerier, MockStorage},
|
||||
OwnedDeps,
|
||||
};
|
||||
use mixnet_contract_common::{GatewayBond, PagedGatewayResponse, QueryMsg};
|
||||
|
||||
// I honestly don't know why we're using this way of querying in tests, but I couldn't be bothered to change it
|
||||
// since I haven't done anything to gateways
|
||||
pub fn get_gateways(deps: &mut OwnedDeps<MockStorage, MockApi, MockQuerier>) -> Vec<GatewayBond> {
|
||||
let result = query(
|
||||
deps.as_ref(),
|
||||
mock_env(),
|
||||
QueryMsg::GetGateways {
|
||||
start_after: None,
|
||||
limit: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let page: PagedGatewayResponse = from_binary(&result).unwrap();
|
||||
if page.start_next_after.is_some() {
|
||||
let next_page = query(
|
||||
deps.as_ref(),
|
||||
mock_env(),
|
||||
QueryMsg::GetGateways {
|
||||
start_after: page.start_next_after,
|
||||
limit: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let next_page: PagedGatewayResponse = from_binary(&next_page).unwrap();
|
||||
if !next_page.nodes.is_empty() {
|
||||
panic!("we didn't manage to get all gateways in a single query")
|
||||
}
|
||||
}
|
||||
page.nodes
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use crate::traits::{
|
||||
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, NodeFamilies, VestingAccount,
|
||||
};
|
||||
use crate::vesting::{populate_vesting_periods, Account};
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use contracts_common::ContractBuildInformation;
|
||||
use cosmwasm_std::{
|
||||
coin, entry_point, to_binary, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
|
||||
@@ -411,7 +412,7 @@ fn try_update_staking_address(
|
||||
/// Bond a gateway, sends [mixnet_contract_common::ExecuteMsg::BondGatewayOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
|
||||
pub fn try_bond_gateway(
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
amount: Coin,
|
||||
info: MessageInfo,
|
||||
env: Env,
|
||||
@@ -448,7 +449,7 @@ pub fn try_track_unbond_gateway(
|
||||
pub fn try_bond_mixnode(
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
amount: Coin,
|
||||
info: MessageInfo,
|
||||
env: Env,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::errors::ContractError;
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use cosmwasm_std::{Coin, Env, Response, Storage};
|
||||
use mixnet_contract_common::{
|
||||
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
|
||||
@@ -12,7 +13,7 @@ pub trait MixnodeBondingAccount {
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
env: &Env,
|
||||
storage: &mut dyn Storage,
|
||||
@@ -50,7 +51,7 @@ pub trait GatewayBondingAccount {
|
||||
fn try_bond_gateway(
|
||||
&self,
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
env: &Env,
|
||||
storage: &mut dyn Storage,
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::PledgeData;
|
||||
use crate::errors::ContractError;
|
||||
use crate::storage::MIXNET_CONTRACT_ADDRESS;
|
||||
use crate::traits::GatewayBondingAccount;
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
|
||||
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
|
||||
use vesting_contract_common::events::{
|
||||
@@ -14,7 +15,7 @@ impl GatewayBondingAccount for Account {
|
||||
fn try_bond_gateway(
|
||||
&self,
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
env: &Env,
|
||||
storage: &mut dyn Storage,
|
||||
|
||||
@@ -5,6 +5,7 @@ use super::Account;
|
||||
use crate::errors::ContractError;
|
||||
use crate::storage::MIXNET_CONTRACT_ADDRESS;
|
||||
use crate::traits::MixnodeBondingAccount;
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
|
||||
use mixnet_contract_common::mixnode::MixNodeConfigUpdate;
|
||||
use mixnet_contract_common::mixnode::MixNodeCostParams;
|
||||
@@ -32,7 +33,7 @@ impl MixnodeBondingAccount for Account {
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
cost_params: MixNodeCostParams,
|
||||
owner_signature: String,
|
||||
owner_signature: MessageSignature,
|
||||
pledge: Coin,
|
||||
env: &Env,
|
||||
storage: &mut dyn Storage,
|
||||
|
||||
@@ -47,6 +47,7 @@ mod tests {
|
||||
use crate::traits::VestingAccount;
|
||||
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
|
||||
use crate::vesting::{populate_vesting_periods, Account};
|
||||
use contracts_common::signing::MessageSignature;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::{coin, coins, Addr, Coin, Timestamp, Uint128};
|
||||
use mixnet_contract_common::mixnode::MixNodeCostParams;
|
||||
@@ -674,7 +675,7 @@ mod tests {
|
||||
let err = account.try_bond_mixnode(
|
||||
mix_node.clone(),
|
||||
cost_params.clone(),
|
||||
"alice".to_string(),
|
||||
MessageSignature::from(vec![1, 2, 3]),
|
||||
Coin {
|
||||
amount: Uint128::new(1_000_000_000_001),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
@@ -687,7 +688,7 @@ mod tests {
|
||||
let ok = account.try_bond_mixnode(
|
||||
mix_node.clone(),
|
||||
cost_params.clone(),
|
||||
"alice".to_string(),
|
||||
MessageSignature::from(vec![1, 2, 3]),
|
||||
Coin {
|
||||
amount: Uint128::new(90_000_000_000),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
@@ -704,7 +705,7 @@ mod tests {
|
||||
let err = account.try_bond_mixnode(
|
||||
mix_node,
|
||||
cost_params,
|
||||
"alice".to_string(),
|
||||
MessageSignature::from(vec![1, 2, 3]),
|
||||
Coin {
|
||||
amount: Uint128::new(10_000_000_001),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
@@ -738,7 +739,7 @@ mod tests {
|
||||
// Try delegating too much
|
||||
let err = account.try_bond_gateway(
|
||||
gateway.clone(),
|
||||
"alice".to_string(),
|
||||
MessageSignature::from(vec![1, 2, 3]),
|
||||
Coin {
|
||||
amount: Uint128::new(1_000_000_000_001),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
@@ -750,7 +751,7 @@ mod tests {
|
||||
|
||||
let ok = account.try_bond_gateway(
|
||||
gateway.clone(),
|
||||
"alice".to_string(),
|
||||
MessageSignature::from(vec![1, 2, 3]),
|
||||
Coin {
|
||||
amount: Uint128::new(90_000_000_000),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
@@ -766,7 +767,7 @@ mod tests {
|
||||
// Try delegating too much again
|
||||
let err = account.try_bond_gateway(
|
||||
gateway,
|
||||
"alice".to_string(),
|
||||
MessageSignature::from(vec![1, 2, 3]),
|
||||
Coin {
|
||||
amount: Uint128::new(500_000_000_001),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "cpu-cycles"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
links = "cpucycles"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.140"
|
||||
|
||||
[build-dependencies]
|
||||
cfg-if = "1"
|
||||
@@ -0,0 +1,65 @@
|
||||
use std::{env, path::PathBuf, process::Command};
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let out_path = PathBuf::from(&out_dir);
|
||||
let source_path = PathBuf::from("libcpucycles")
|
||||
.canonicalize()
|
||||
.expect("cannot canonicalize path");
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "mips", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "arm")))] {
|
||||
panic!("Unsupported architecture - {}!", env::var("CARGO_CFG_TARGET_ARCH").unwrap(), )
|
||||
}
|
||||
};
|
||||
|
||||
let mut compile_o_command = Command::new("./configure");
|
||||
let compile_o_command = compile_o_command
|
||||
.current_dir(&source_path)
|
||||
.arg(format!("--prefix={out_dir}"));
|
||||
|
||||
match compile_o_command.output() {
|
||||
Ok(output) => {
|
||||
if !output.status.success() {
|
||||
panic!("{:?}", unsafe {
|
||||
std::str::from_utf8_unchecked(&output.stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("{e}"),
|
||||
}
|
||||
|
||||
let mut compile_o_command = Command::new("make");
|
||||
let compile_o_command = compile_o_command.current_dir(&source_path).arg("install");
|
||||
|
||||
match compile_o_command.output() {
|
||||
Ok(output) => {
|
||||
if !output.status.success() {
|
||||
panic!("{:?}", unsafe {
|
||||
std::str::from_utf8_unchecked(&output.stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("{e}"),
|
||||
}
|
||||
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
out_path.join("lib").to_str().unwrap()
|
||||
);
|
||||
println!("cargo:rustc-link-lib=static=cpucycles");
|
||||
|
||||
let mut compile_o_command = Command::new("make");
|
||||
let compile_o_command = compile_o_command.current_dir(source_path).arg("clean");
|
||||
|
||||
match compile_o_command.output() {
|
||||
Ok(output) => {
|
||||
if !output.status.success() {
|
||||
panic!("{:?}", unsafe {
|
||||
std::str::from_utf8_unchecked(&output.stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("{e}"),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
default:
|
||||
cd build && $(MAKE)
|
||||
|
||||
install:
|
||||
cd build && $(MAKE) install
|
||||
|
||||
clean:
|
||||
cd build && $(MAKE) clean
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import datetime
|
||||
import markdown
|
||||
|
||||
def load(fn):
|
||||
with open(fn) as f:
|
||||
return f.read()
|
||||
|
||||
style = load('autogen/html-style')
|
||||
sitetitle = load('autogen/html-title')
|
||||
|
||||
files = []
|
||||
|
||||
with open('autogen/html-files') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
line = line.split(':')
|
||||
if len(line) != 3: continue
|
||||
files += [line]
|
||||
|
||||
for md,html,pagetitle in files:
|
||||
fnmd = 'doc/%s.md' % md
|
||||
fnhtml = 'doc/html/%s.html' % html
|
||||
output = ''
|
||||
|
||||
x = load(fnmd)
|
||||
x = markdown.markdown(x,extensions=['markdown.extensions.extra','markdown.extensions.tables'])
|
||||
mtime = datetime.datetime.utcfromtimestamp(os.path.getmtime(fnmd)).strftime('%Y.%m.%d')
|
||||
|
||||
output += '<html>\n<head>\n'
|
||||
output += style
|
||||
output += '<title>\n'
|
||||
output += pagetitle
|
||||
output += '</title>\n'
|
||||
output += '</head>\n'
|
||||
output += '<body>\n'
|
||||
|
||||
output += '<div class=headline>\n'
|
||||
output += sitetitle
|
||||
output += '</div>\n'
|
||||
|
||||
output += '<div class=nav>\n'
|
||||
for submd,subhtml,subpagetitle in files:
|
||||
if subhtml == html:
|
||||
output += '<div class="navt here">'
|
||||
output += pagetitle+'\n'
|
||||
else:
|
||||
output += '<div class="navt away">'
|
||||
output += '<a href=%s.html>%s</a>\n' % (subhtml,subpagetitle)
|
||||
output += '</div>'
|
||||
output += '</div>\n'
|
||||
|
||||
output += '<div class=main>\n'
|
||||
output += x
|
||||
output += '<hr><font size=1><b>Version:</b>\n'
|
||||
output += 'This is version %s of the "%s" web page.\n' % (mtime,pagetitle)
|
||||
output += '</font>\n'
|
||||
output += '</div>\n'
|
||||
|
||||
output += '</body>\n'
|
||||
output += '</html>\n'
|
||||
|
||||
if not os.path.exists(fnhtml) or output != load(fnhtml):
|
||||
with open(fnhtml+'.new','w') as f:
|
||||
f.write(output)
|
||||
os.chmod(fnhtml+'.new',0o444)
|
||||
os.rename(fnhtml+'.new',fnhtml)
|
||||
@@ -0,0 +1,7 @@
|
||||
readme:index:Intro
|
||||
download:download:Download
|
||||
install:install:Install
|
||||
api:api:API
|
||||
counters:counters:Counters
|
||||
selection:selection:Selection
|
||||
security:security:Security
|
||||
@@ -0,0 +1,32 @@
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
html{overflow-y:scroll}
|
||||
body{font-family:sans-serif}
|
||||
p,ul,ol,blockquote,pre{font-size:0.9em;line-height:1.6em}
|
||||
li p{font-size:1.0em}
|
||||
blockquote p{font-size:1.0em}
|
||||
tt{font-size:1.2em}
|
||||
code{font-size:1.2em}
|
||||
h1{font-size:1.5em}
|
||||
h2{font-size:1.3em}
|
||||
h3{font-size:1.0em}
|
||||
h1 a{text-decoration:none}
|
||||
table{border-collapse:collapse}
|
||||
th,td{border:1px solid black}
|
||||
table a{text-decoration:none}
|
||||
table tr{font-size:0.9em;line-height:1.6em}
|
||||
.links a:hover{text-decoration:underline}
|
||||
.links a:active{text-decoration:underline}
|
||||
.links img{width:200px;padding-left:1em}
|
||||
.links td{border:0px;padding-top:0.5em;padding-bottom:0.5em}
|
||||
.headline{padding:0;font-weight:bold;font-size:1.5em;vertical-align:top;padding-bottom:0.5em;color:#125d0d}
|
||||
.navt{display:inline-block;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;
|
||||
min-width:14%;margin:0;padding:0;padding-left:0.5em;padding-right:0.5em;vertical-align:center;
|
||||
font-weight:bold;font-size:1.1em;text-align:center;border:1px solid black}
|
||||
.here{border-bottom:0px;background-color:#ffffff}
|
||||
.away{background-color:#125d0d;}
|
||||
.away a{text-decoration:none;display:block;color:#ffffff}
|
||||
.away a:hover,.away a:active{text-decoration:underline}
|
||||
.main{margin:0;padding-top:0em;padding-bottom:1%;clear:both}
|
||||
</style>
|
||||
@@ -0,0 +1 @@
|
||||
libcpucycles
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
pandoc --standalone --to man --metadata title=cpucycles --metadata section=3 < doc/api.md > doc/man/cpucycles.3
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <cpucycles.h>
|
||||
|
||||
#define TIMINGS 63
|
||||
static long long t[TIMINGS+1];
|
||||
|
||||
static void t_print(void)
|
||||
{
|
||||
long long median = 0;
|
||||
long long i,j;
|
||||
|
||||
for (i = 0;i < TIMINGS;++i)
|
||||
t[i] = t[i+1]-t[i];
|
||||
for (j = 0;j < TIMINGS;++j) {
|
||||
long long belowj = 0;
|
||||
long long abovej = 0;
|
||||
for (i = 0;i < TIMINGS;++i) if (t[i] < t[j]) ++belowj;
|
||||
for (i = 0;i < TIMINGS;++i) if (t[i] > t[j]) ++abovej;
|
||||
if (belowj*2 < TIMINGS && abovej*2 < TIMINGS) {
|
||||
median = t[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf(" %lld ",median);
|
||||
for (i = 0;i < TIMINGS;++i)
|
||||
printf("%+lld",t[i]-median);
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static long long microseconds(void)
|
||||
{
|
||||
struct timeval t;
|
||||
long long result;
|
||||
gettimeofday(&t,(struct timezone *) 0);
|
||||
result = t.tv_sec;
|
||||
result *= 1000000;
|
||||
result += t.tv_usec;
|
||||
return result;
|
||||
}
|
||||
|
||||
static volatile int v;
|
||||
|
||||
static void measure_cpucycles(void)
|
||||
{
|
||||
long long loops,i,j;
|
||||
|
||||
printf("cpucycles persecond %lld\n",cpucycles_persecond());
|
||||
printf("cpucycles implementation %s\n",cpucycles_implementation());
|
||||
|
||||
for (i = 0;i <= TIMINGS;++i)
|
||||
t[i] = cpucycles();
|
||||
printf("cpucycles median"); t_print();
|
||||
|
||||
for (loops = 1024;loops <= 1048576;loops *= 2) {
|
||||
long long t00,t01,t10,t11;
|
||||
long long m0,m1;
|
||||
double ratiobelow,ratioabove;
|
||||
|
||||
t00 = cpucycles();
|
||||
m0 = microseconds();
|
||||
t01 = cpucycles();
|
||||
|
||||
for (j = 0;j < loops;++j) v = 0;
|
||||
|
||||
t10 = cpucycles();
|
||||
m1 = microseconds();
|
||||
t11 = cpucycles();
|
||||
|
||||
if (t01 < t00) continue;
|
||||
if (t10 < t01) continue;
|
||||
if (t11 < t10) continue;
|
||||
if (m1 <= m0+2) continue;
|
||||
|
||||
ratiobelow = floor((1000000.0*(t10-t01))/(m1+1-m0));
|
||||
ratioabove = ceil((1000000.0*(t11-t00))/(m1-m0-1));
|
||||
|
||||
printf("cpucycles observed persecond %.0lf...%.0lf with %lld loops %lld microseconds\n",ratiobelow,ratioabove,loops,m1-m0);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
cpucycles_tracesetup();
|
||||
printf("cpucycles version %s\n",cpucycles_version());
|
||||
measure_cpucycles();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
gcc -Wall -fPIC -fwrapv -O -fvisibility=hidden
|
||||
clang -Wall -fPIC -fwrapv -Qunused-arguments -O -fvisibility=hidden
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import platform
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
prefix = '/usr/local'
|
||||
clean = True
|
||||
linktype = 'so'
|
||||
|
||||
host = platform.machine()
|
||||
host = ''.join(c for c in host if c in '_0123456789abcdefghijklmnopqrstuvwxyz')
|
||||
|
||||
if host == 'x86_64': host = 'amd64'
|
||||
if host == 'i386': host = 'x86'
|
||||
if host == 'i686': host = 'x86'
|
||||
|
||||
if host.startswith('armv8') or host.startswith('aarch64'): host = 'arm64'
|
||||
if host.startswith('arm'): host = 'arm32'
|
||||
|
||||
if host.startswith('riscv64'): host = 'riscv64'
|
||||
if host.startswith('riscv'): host = 'riscv32'
|
||||
|
||||
if host.startswith('mips64'): host = 'mips64'
|
||||
if host.startswith('mips'): host = 'mips32'
|
||||
|
||||
if host.startswith('powerpc64') or host.startswith('ppc64'): host = 'ppc64'
|
||||
if host.startswith('powerpc') or host.startswith('ppc'): host = 'ppc32'
|
||||
|
||||
if host.startswith('sparcv9') or host.startswith('sun4u'): host = 'sparc64'
|
||||
if host.startswith('sparc') or host.startswith('sun'): host = 'sparc32'
|
||||
|
||||
makefile = ''
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if arg.startswith('--prefix='):
|
||||
prefix = arg[9:]
|
||||
continue
|
||||
if arg.startswith('--host='):
|
||||
host = arg[7:]
|
||||
host = host.split('-')[0]
|
||||
continue
|
||||
if arg == '--clean':
|
||||
clean = True
|
||||
continue
|
||||
if arg == '--noclean':
|
||||
clean = False
|
||||
continue
|
||||
raise ValueError('unrecognized argument %s' % arg)
|
||||
|
||||
echoargs = './configure'
|
||||
echoargs += ' --prefix=%s' % prefix
|
||||
echoargs += ' --host=%s' % host
|
||||
if clean: echoargs += ' --clean'
|
||||
if not clean: echoargs += ' --noclean'
|
||||
print(echoargs)
|
||||
|
||||
if prefix[0] != '/':
|
||||
raise ValueError('prefix %s is not an absolute path' % prefix)
|
||||
|
||||
rpath = None
|
||||
# XXX: rpath = '%s/lib' % prefix
|
||||
|
||||
if clean:
|
||||
shutil.rmtree('build/%s' % host,ignore_errors=True)
|
||||
|
||||
def dirlinksym(dir,source,target):
|
||||
with tempfile.TemporaryDirectory(dir=dir) as t:
|
||||
os.symlink(target,'%s/symlink' % t)
|
||||
os.rename('%s/symlink' % t,'%s/%s' % (dir,source))
|
||||
|
||||
os.makedirs('build/%s' % host,exist_ok=True)
|
||||
os.makedirs('build/%s/package/bin' % host,exist_ok=True)
|
||||
os.makedirs('build/%s/package/lib' % host,exist_ok=True)
|
||||
os.makedirs('build/%s/package/include' % host,exist_ok=True)
|
||||
|
||||
if clean:
|
||||
os.symlink('../..','build/%s/src' % host)
|
||||
|
||||
# ----- build scripts
|
||||
|
||||
os.makedirs('build/%s/scripts'%host,exist_ok=True)
|
||||
dirlinksym('build/%s/scripts'%host,'install','../src/scripts-build/install')
|
||||
|
||||
# ----- compilers
|
||||
|
||||
def compilerversion(c):
|
||||
try:
|
||||
p = subprocess.Popen(c.split()+['--version'],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,universal_newlines=True)
|
||||
out,err = p.communicate()
|
||||
assert not err
|
||||
assert not p.returncode
|
||||
return out
|
||||
except:
|
||||
pass
|
||||
|
||||
firstcompiler = None
|
||||
|
||||
with open('compilers/default') as f:
|
||||
for c in f.readlines():
|
||||
c = c.strip()
|
||||
cv = compilerversion(c)
|
||||
if cv == None:
|
||||
print('skipping default compiler %s' % c)
|
||||
continue
|
||||
print('using default compiler %s' % c)
|
||||
firstcompiler = c
|
||||
break
|
||||
|
||||
if firstcompiler is None:
|
||||
raise ValueError('did not find a working compiler')
|
||||
|
||||
with open('build/%s/scripts/compiledefault' % host,'w') as f:
|
||||
f.write('#!/bin/sh\n')
|
||||
f.write('\n')
|
||||
f.write('dir="$1"; shift\n')
|
||||
f.write('base="$1"; shift\n')
|
||||
f.write('ext="$1"; shift\n')
|
||||
f.write('\n')
|
||||
f.write('cd "$dir" && \\\n')
|
||||
f.write('%s \\\n' % firstcompiler)
|
||||
f.write(' "$@" \\\n')
|
||||
f.write(' -c "$base.$ext"\n')
|
||||
os.chmod('build/%s/scripts/compiledefault' % host,0o755)
|
||||
|
||||
# ----- libcpucycles
|
||||
|
||||
os.makedirs('build/%s/cpucycles' % host,exist_ok=True)
|
||||
os.makedirs('build/%s/package/man/man3' % host,exist_ok=True)
|
||||
|
||||
dirlinksym('build/%s/cpucycles'%host,'cpucycles.h','../src/cpucycles/cpucycles.h')
|
||||
dirlinksym('build/%s/cpucycles'%host,'cpucycles_internal.h','../src/cpucycles/cpucycles_internal.h')
|
||||
shutil.copy2('cpucycles/cpucycles.h','build/%s/package/include/cpucycles.h'%host)
|
||||
shutil.copy2('doc/man/cpucycles.3','build/%s/package/man/man3/cpucycles.3'%host)
|
||||
|
||||
with open('build/%s/cpucycles/compile-ticks' % host,'w') as f:
|
||||
f.write('#!/bin/sh\n')
|
||||
f.write('arch="$1"; shift\n')
|
||||
f.write('x="$1"; shift\n')
|
||||
f.write('for source in try-"$arch"-"$x".c try-default-zero.c\n')
|
||||
f.write('do\n')
|
||||
f.write(' cp "$source" "$arch"-"$x".c\n')
|
||||
f.write(' %s \\\n' % firstcompiler)
|
||||
f.write(' -Dticks=cpucycles_ticks_"$arch"_"$x" \\\n')
|
||||
f.write(' -Dticks_setup=cpucycles_ticks_"$arch"_"$x"_setup \\\n')
|
||||
f.write(' -c "$arch"-"$x".c\n')
|
||||
f.write(' case $? in\n')
|
||||
f.write(' 0) break ;;\n')
|
||||
f.write(' 111) exit 111 ;;\n')
|
||||
f.write(' *) echo "skipping option that did not compile" ;;\n')
|
||||
f.write(' esac\n')
|
||||
f.write('done\n')
|
||||
os.chmod('build/%s/cpucycles/compile-ticks' % host,0o755)
|
||||
|
||||
cpucyclesoptions = []
|
||||
cpucyclesofiles = []
|
||||
|
||||
with open('cpucycles/options') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line == '': continue
|
||||
if line[0] == '#': continue
|
||||
base = line.split()[0]
|
||||
if not os.path.exists('cpucycles/%s.c' % base): continue
|
||||
cpucycles = base.split('-')
|
||||
if len(cpucycles) != 2: continue
|
||||
if cpucycles[0] not in (host,'default'): continue
|
||||
cpucyclesoptions += [cpucycles]
|
||||
|
||||
cpucyclesoptions += [['default','zero']] # must be last
|
||||
|
||||
for cpucycles in cpucyclesoptions:
|
||||
base = '-'.join(cpucycles)
|
||||
cpucyclesofiles += ['cpucycles/%s.o' % base]
|
||||
dirlinksym('build/%s/cpucycles'%host,'try-%s.c'%base,'../src/cpucycles/%s.c'%base)
|
||||
M = 'cpucycles/%s.o: cpucycles/try-%s.c cpucycles/try-default-zero.c\n' % (base,base)
|
||||
M += '\tcd cpucycles && ./compile-ticks %s %s\n' % tuple(cpucycles)
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
|
||||
for fn in sorted(os.listdir('cpucycles')):
|
||||
if not fn.endswith('.c'): continue
|
||||
if '-' in fn: continue
|
||||
base = fn[:-2]
|
||||
cpucyclesofiles += ['cpucycles/%s.o' % base]
|
||||
dirlinksym('build/%s/cpucycles'%host,fn,'../src/cpucycles/%s'%fn)
|
||||
M = 'cpucycles/%s.o: cpucycles/%s.c\n' % (base,base)
|
||||
M += '\tscripts/compiledefault cpucycles %s c\n' % base
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
|
||||
with open('build/%s/cpucycles/options.inc' % host,'w') as f:
|
||||
f.write('#define NUMOPTIONS %d\n' % len(cpucyclesoptions))
|
||||
f.write('#define DEFAULTOPTION (NUMOPTIONS-1)\n')
|
||||
f.write('\n')
|
||||
for cpucycles in cpucyclesoptions:
|
||||
f.write('extern long long cpucycles_ticks_%s_%s_setup(void);\n' % (cpucycles[0],cpucycles[1]))
|
||||
f.write('extern long long cpucycles_ticks_%s_%s(void);\n' % (cpucycles[0],cpucycles[1]))
|
||||
f.write('\n')
|
||||
f.write('static struct {\n')
|
||||
f.write(' const char *implementation;\n')
|
||||
f.write(' long long (*ticks_setup)(void);\n')
|
||||
f.write(' long long (*ticks)(void);\n')
|
||||
f.write('} options[NUMOPTIONS] = {\n')
|
||||
for cpucycles in cpucyclesoptions:
|
||||
f.write('{ "%s-%s", cpucycles_ticks_%s_%s_setup, cpucycles_ticks_%s_%s },\n' % (cpucycles[0],cpucycles[1],cpucycles[0],cpucycles[1],cpucycles[0],cpucycles[1]))
|
||||
f.write('} ;\n')
|
||||
|
||||
dirlinksym('build/%s/scripts'%host,'staticlib','../src/scripts-build/staticlib')
|
||||
|
||||
M = 'package/lib/libcpucycles.a: scripts/staticlib %s\n' % ' '.join(cpucyclesofiles)
|
||||
M += '\tscripts/staticlib %s\n' % ' '.join(cpucyclesofiles)
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
|
||||
with open('build/%s/scripts/sharedlib' % host,'w') as f:
|
||||
f.write('#!/bin/sh\n')
|
||||
f.write('\n')
|
||||
f.write('%s -shared \\\n' % firstcompiler)
|
||||
if rpath:
|
||||
f.write(' -Wl,-rpath=%s \\\n' % rpath)
|
||||
f.write(' -Wl,-soname,libcpucycles.so.1 \\\n')
|
||||
f.write(' -o package/lib/libcpucycles.so.1 \\\n')
|
||||
f.write(' "$@"\n')
|
||||
f.write('chmod 644 package/lib/libcpucycles.so.1\n')
|
||||
os.chmod('build/%s/scripts/sharedlib' % host,0o755)
|
||||
|
||||
M = 'package/lib/libcpucycles.so.1: scripts/sharedlib %s\n' % ' '.join(cpucyclesofiles)
|
||||
M += '\tscripts/sharedlib %s\n' % ' '.join(cpucyclesofiles)
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
|
||||
M = 'package/lib/libcpucycles.so: package/lib/libcpucycles.so.1\n'
|
||||
M += '\trm -f package/lib/libcpucycles.so\n'
|
||||
M += '\tln -s libcpucycles.so.1 package/lib/libcpucycles.so\n'
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
|
||||
# ----- command
|
||||
|
||||
os.makedirs('build/%s/command'%host)
|
||||
for c in sorted(os.listdir('command')):
|
||||
dirlinksym('build/%s/command'%host,c,'../src/command/%s'%c)
|
||||
dirlinksym('build/%s/command'%host,'bin','../package/bin')
|
||||
dirlinksym('build/%s/command'%host,'lib','../package/lib')
|
||||
dirlinksym('build/%s/command'%host,'include','../package/include')
|
||||
|
||||
with open('build/%s/command/link' % host,'w') as f:
|
||||
f.write('#!/bin/sh\n')
|
||||
f.write('target="$1"; shift\n')
|
||||
f.write('%s \\\n' % firstcompiler)
|
||||
f.write(' -o "$target" "$@"\n')
|
||||
os.chmod('build/%s/command/link' % host,0o755)
|
||||
|
||||
commands = []
|
||||
|
||||
for fn in sorted(os.listdir('command')):
|
||||
if not fn.endswith('.c'): continue
|
||||
|
||||
libs = ['libcpucycles']
|
||||
|
||||
base = fn[:-2]
|
||||
M = 'command/%s.o: command/%s.c\n' % (base,base)
|
||||
M += '\tscripts/compiledefault command %s c -I include\n' % base
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
M = 'package/bin/%s: command/%s.o%s\n' % (base,base,''.join(' package/lib/%s.%s' % (x,linktype) for x in libs))
|
||||
M += '\tcd command && ./link bin/%s %s.o%s -lm -lrt\n' % (base,base,''.join(' lib/%s.%s' % (x,linktype) for x in libs))
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
commands += ['package/bin/%s' % base]
|
||||
|
||||
M = 'commands: %s\n' % ' '.join(commands)
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
|
||||
# ----- make install
|
||||
|
||||
M = 'install: scripts/install default\n'
|
||||
M += '\tscripts/install %s\n' % prefix
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
|
||||
# ----- make default
|
||||
|
||||
M = 'default: package/lib/libcpucycles.a package/lib/libcpucycles.so package/lib/libcpucycles.so.1 \\\n'
|
||||
M += 'commands\n'
|
||||
M += '\n'
|
||||
makefile = M + makefile
|
||||
|
||||
with open('build/%s/Makefile' % host,'w') as f:
|
||||
f.write(makefile)
|
||||
|
||||
# ----- build/0, build/Makefile
|
||||
|
||||
dirlinksym('build','0',host)
|
||||
|
||||
with open('build/Makefile','w') as f:
|
||||
f.write('default:\n')
|
||||
f.write('\tcd %s && $(MAKE)\n' % host)
|
||||
f.write('\n')
|
||||
f.write('install:\n')
|
||||
f.write('\tcd %s && $(MAKE) install\n' % host)
|
||||
f.write('\n')
|
||||
f.write('clean:\n')
|
||||
f.write('\trm -r %s\n' % host)
|
||||
@@ -0,0 +1,53 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/amd64rdpmc.c
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
static struct perf_event_attr attr;
|
||||
static int fdperf = -1;
|
||||
static struct perf_event_mmap_page *buf = 0;
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
long long result;
|
||||
unsigned int seq;
|
||||
long long index;
|
||||
long long offset;
|
||||
|
||||
do {
|
||||
seq = buf->lock;
|
||||
asm volatile("" ::: "memory");
|
||||
index = buf->index;
|
||||
offset = buf->offset;
|
||||
asm volatile("rdpmc;shlq $32,%%rdx;orq %%rdx,%%rax"
|
||||
: "=a"(result) : "c"(index-1) : "%rdx");
|
||||
asm volatile("" ::: "memory");
|
||||
} while (buf->lock != seq);
|
||||
|
||||
result += offset;
|
||||
result &= 0xffffffffffff;
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (fdperf == -1) {
|
||||
attr.type = PERF_TYPE_HARDWARE;
|
||||
attr.config = PERF_COUNT_HW_CPU_CYCLES;
|
||||
attr.exclude_kernel = 1;
|
||||
fdperf = syscall(__NR_perf_event_open,&attr,0,-1,-1,0);
|
||||
if (fdperf == -1) return cpucycles_SKIP;
|
||||
buf = mmap(NULL,sysconf(_SC_PAGESIZE),PROT_READ,MAP_SHARED,fdperf,0);
|
||||
}
|
||||
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_CYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
return __rdtsc();
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_MAYBECYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/amd64tscfreq.c
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
unsigned long long result;
|
||||
asm volatile(".byte 15;.byte 49;shlq $32,%%rdx;orq %%rdx,%%rax"
|
||||
: "=a"(result) :: "%rdx");
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_MAYBECYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/cortex.c
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
unsigned int result;
|
||||
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(result));
|
||||
return (unsigned long long) result;
|
||||
}
|
||||
|
||||
static long enable(void)
|
||||
{
|
||||
asm volatile("mcr p15, 0, %0, c9, c12, 0" :: "r"(17));
|
||||
asm volatile("mcr p15, 0, %0, c9, c12, 1" :: "r"(0x8000000f));
|
||||
asm volatile("mcr p15, 0, %0, c9, c12, 3" :: "r"(0x8000000f));
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(enable)) return cpucycles_SKIP;
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_EXTEND32;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/armv8.c
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
long long result;
|
||||
asm volatile("mrs %0, PMCCNTR_EL0" : "=r" (result));
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_CYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/vct.c
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
long long result;
|
||||
asm volatile("mrs %0, CNTVCT_EL0" : "=r" (result));
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_FINDMULTIPLIER;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// version 20230115
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
// 20230115 djb: cpucycles_version()
|
||||
// 20230114 djb: improve punctuation
|
||||
|
||||
#ifndef cpucycles_h
|
||||
#define cpucycles_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern long long (*cpucycles)(void) __attribute__((visibility("default")));
|
||||
extern const char *cpucycles_implementation(void) __attribute__((visibility("default")));
|
||||
extern const char *cpucycles_version(void) __attribute__((visibility("default")));
|
||||
extern long long cpucycles_persecond(void) __attribute__((visibility("default")));
|
||||
extern void cpucycles_tracesetup(void) __attribute__((visibility("default")));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
#ifndef cpucycles_internal_h
|
||||
#define cpucycles_internal_h
|
||||
|
||||
extern long long cpucycles_init(void);
|
||||
extern long long cpucycles_microseconds(void);
|
||||
extern int cpucycles_works(long long (*)(void));
|
||||
|
||||
// return values from ticks_setup():
|
||||
#define cpucycles_SKIP (0)
|
||||
#define cpucycles_CYCLECOUNTER (-1)
|
||||
#define cpucycles_MAYBECYCLECOUNTER (-2)
|
||||
#define cpucycles_FINDMULTIPLIER (-3)
|
||||
#define cpucycles_EXTEND32 (-32)
|
||||
// and positive values mean known ticks/second
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
return 1000000;
|
||||
}
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
return cpucycles_microseconds();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
return mach_absolute_time();
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_FINDMULTIPLIER;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/monotonic.c
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
return 1000000000;
|
||||
}
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
struct timespec t;
|
||||
long long result;
|
||||
clock_gettime(CLOCK_MONOTONIC,&t);
|
||||
result = t.tv_sec;
|
||||
result *= 1000000000;
|
||||
result += t.tv_nsec;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// version 20230106
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/perfevent.c
|
||||
|
||||
// 20230106 djb: read() into int64_t instead of long long
|
||||
// 20230106 djb: add comment on RUNNING/ENABLED
|
||||
|
||||
/*
|
||||
This code intentionally avoids dividing by the
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING/ENABLED ratio.
|
||||
|
||||
The motivation for that ratio is as follows:
|
||||
|
||||
* A typical CPU has a limited number of performance-monitoring
|
||||
counters active at once. For example, there are 8 "programmable"
|
||||
counters on Intel Skylake.
|
||||
|
||||
* "perf stat" allows the user to enable more counters. The OS kernel
|
||||
periodically (e.g., every millisecond) changes the limited number of
|
||||
active hardware counters to a new subset of the enabled counters, and
|
||||
"perf stat" reports PERF_FORMAT_TOTAL_TIME_RUNNING/ENABLED for each
|
||||
counter, the fraction of time spent with that counter running.
|
||||
|
||||
For long-running programs, dividing the hardware counter by
|
||||
RUNNING/ENABLED usually produces a reasonable estimate of what the count
|
||||
would have been without competition from other counters.
|
||||
|
||||
A fixable problem with this multiplexing of counters is that the kernel
|
||||
appears to simply cycle through counters, so unlucky programs can
|
||||
trigger moiré effects. The fix is to select random subsets of counters.
|
||||
|
||||
A more fundamental problem is that cpucycles() has to be usable for
|
||||
timing short subroutines, including subroutines so short that the OS has
|
||||
no opportunity to change from one selection of counters to another. Say
|
||||
RUNNING is 0; should cpucycles() then divide by 0?
|
||||
|
||||
If a caller runs cpucycles(), X(), cpucycles(), X(), etc., and the cycle
|
||||
counter happens to be enabled for only 80% of the runs of X(), then
|
||||
simply computing the median difference of adjacent cycle counts, with no
|
||||
scaling, will filter out the zeros and correctly compute the cost of X.
|
||||
Averages won't (without scaling), but averages have other problems, such
|
||||
as being heavily influenced by interrupts. (Omitting kernel time from
|
||||
perf results does not remove the influence of interrupts on caches.)
|
||||
|
||||
Given the importance of cycle counting, it is better to have cycle
|
||||
counters always running. For example, on Skylake, Intel provides the 8
|
||||
"programmable" counters on top of a separate cycle counter ("fixed
|
||||
counter 1"), so there is no good reason for the kernel to waste a
|
||||
"programmable" counter on a cycle counter, there is no good reason to
|
||||
turn the cycle counter off, and there is no good reason for RUNNING to
|
||||
be below ENABLED for the cycle counter.
|
||||
|
||||
Of course, applications that use just one performance counter at a time
|
||||
don't have to worry about kernels getting this wrong, and don't have to
|
||||
worry about the possibility of getting noisy or invalid results on CPUs
|
||||
that have heavier constraints on the number of simultaneous counters.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
static int fddev = -1;
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
int64_t result;
|
||||
|
||||
if (read(fddev,&result,sizeof result) < sizeof result) return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (fddev == -1) {
|
||||
static struct perf_event_attr attr;
|
||||
|
||||
memset(&attr,0,sizeof attr);
|
||||
attr.type = PERF_TYPE_HARDWARE;
|
||||
attr.size = sizeof(struct perf_event_attr);
|
||||
attr.config = PERF_COUNT_HW_CPU_CYCLES;
|
||||
attr.disabled = 1;
|
||||
attr.exclude_kernel = 1;
|
||||
attr.exclude_hv = 1;
|
||||
|
||||
fddev = syscall(__NR_perf_event_open,&attr,0,-1,-1,0);
|
||||
if (fddev == -1) return cpucycles_SKIP;
|
||||
|
||||
ioctl(fddev,PERF_EVENT_IOC_RESET,0);
|
||||
ioctl(fddev,PERF_EVENT_IOC_ENABLE,0);
|
||||
}
|
||||
|
||||
return cpucycles_MAYBECYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
return cpucycles_SKIP;
|
||||
}
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/mips.c
|
||||
|
||||
// mips32 release 2 instruction rdhwr
|
||||
// 7c02103b: read hwr#2 (cycle count) into $2
|
||||
// 7c02183b: read hwr#3 (cycle-count multiplier) into $2
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
static unsigned int multiplier = 0;
|
||||
|
||||
static long long multiplier_set(void)
|
||||
{
|
||||
asm volatile(".long 0x7c02183b; move %0,$2" : "=r"(multiplier) : : "$2");
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
unsigned int result;
|
||||
asm volatile(".long 0x7c02103b; move %0,$2" : "=r"(result) :: "$2");
|
||||
result *= multiplier;
|
||||
return (unsigned long long) result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(multiplier_set)) return cpucycles_SKIP;
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_EXTEND32;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
amd64-pmc
|
||||
amd64-tsc
|
||||
amd64-tscasm
|
||||
arm32-cortex
|
||||
arm64-pmc
|
||||
arm64-vct
|
||||
mips64-cc
|
||||
ppc32-mftb
|
||||
ppc64-mftb
|
||||
riscv32-rdcycle
|
||||
riscv64-rdcycle
|
||||
s390x-stckf
|
||||
sparc64-rdtick
|
||||
x86-tsc
|
||||
x86-tscasm
|
||||
default-perfevent
|
||||
default-mach
|
||||
default-monotonic
|
||||
default-gettimeofday
|
||||
@@ -0,0 +1,30 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/powerpccpuinfo.c
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
unsigned int high, low, newhigh;
|
||||
unsigned long long result;
|
||||
|
||||
do {
|
||||
asm volatile(
|
||||
"mftbu %0; mftb %1; mftbu %2"
|
||||
: "=r" (high), "=r" (low), "=r" (newhigh)
|
||||
);
|
||||
} while (newhigh != high);
|
||||
|
||||
result = high;
|
||||
result <<= 32;
|
||||
result |= low;
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_FINDMULTIPLIER;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
// adapted from supercop/cpucycles/powerpccpuinfo.c
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
unsigned int high, low, newhigh;
|
||||
unsigned long long result;
|
||||
|
||||
do {
|
||||
asm volatile(
|
||||
"mftbu %0; mftb %1; mftbu %2"
|
||||
: "=r" (high), "=r" (low), "=r" (newhigh)
|
||||
);
|
||||
} while (newhigh != high);
|
||||
|
||||
result = high;
|
||||
result <<= 32;
|
||||
result |= low;
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_FINDMULTIPLIER;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
// adapted from supercop/cpucycles/riscv.c
|
||||
// which has code from djb and Romain Dolbeau
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
#ifndef __riscv_xlen
|
||||
#error this code is only for riscv platforms
|
||||
#endif
|
||||
|
||||
#if __riscv_xlen != 32
|
||||
#error this code is only for riscv32 platforms
|
||||
#endif
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
unsigned int low, high, newhigh;
|
||||
unsigned long long result;
|
||||
|
||||
asm volatile( "start%=:\n"
|
||||
"rdcycleh %0\n"
|
||||
"rdcycle %1\n"
|
||||
"rdcycleh %2\n"
|
||||
"bne %0, %2, start%=\n"
|
||||
: "=r"(high), "=r"(low), "=r"(newhigh));
|
||||
result = high;
|
||||
result <<= 32;
|
||||
result |= low;
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_CYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
// adapted from supercop/cpucycles/riscv.c
|
||||
// which has code from djb and Romain Dolbeau
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
#ifndef __riscv_xlen
|
||||
#error this code is only for riscv platforms
|
||||
#endif
|
||||
|
||||
#if __riscv_xlen != 64
|
||||
#error this code is only for riscv64 platforms
|
||||
#endif
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
long long result;
|
||||
asm volatile("rdcycle %0" : "=r" (result));
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_CYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// version 20230106
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
// adapted from sparc64-rdtick.c
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
long long result;
|
||||
asm volatile("stckf 0(%0)" :: "a"(&result) : "memory","cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return 4096000000; // manual says 2^12 per microsecond
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
// adapted from supercop/cpucycles/sparccpuinfo.c
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
#if defined(__sparcv8) || defined(__sparcv8plus)
|
||||
#error this code is only for sparc64 platforms
|
||||
#endif
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
long long result;
|
||||
asm volatile("rd %%tick,%0" : "=r" (result));
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_CYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
// version 20230115
|
||||
// public domain
|
||||
// djb
|
||||
// includes some pieces adapted from supercop
|
||||
|
||||
// 20230115 djb: cpucycles_version()
|
||||
// 20230106 djb: support "cpu MHz static" (ibm z15)
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
#include "cpucycles.h"
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
static int tracesetup = 0;
|
||||
|
||||
void cpucycles_tracesetup(void)
|
||||
{
|
||||
tracesetup = 1;
|
||||
}
|
||||
|
||||
static jmp_buf crash_jmp;
|
||||
|
||||
static void crash(int s)
|
||||
{
|
||||
siglongjmp(crash_jmp,0);
|
||||
}
|
||||
|
||||
int cpucycles_works(long long (*ticks)(void))
|
||||
{
|
||||
volatile int result = 0;
|
||||
struct sigaction old_sigill;
|
||||
struct sigaction old_sigfpe;
|
||||
struct sigaction old_sigbus;
|
||||
struct sigaction old_sigsegv;
|
||||
struct sigaction crash_action;
|
||||
|
||||
memset(&crash_action,0,sizeof crash_action);
|
||||
crash_action.sa_handler = crash;
|
||||
|
||||
sigaction(SIGILL,0,&old_sigill);
|
||||
sigaction(SIGFPE,0,&old_sigfpe);
|
||||
sigaction(SIGBUS,0,&old_sigbus);
|
||||
sigaction(SIGSEGV,0,&old_sigsegv);
|
||||
|
||||
if (!sigsetjmp(crash_jmp,1)) {
|
||||
sigaction(SIGILL,&crash_action,0);
|
||||
sigaction(SIGFPE,&crash_action,0);
|
||||
sigaction(SIGBUS,&crash_action,0);
|
||||
sigaction(SIGSEGV,&crash_action,0);
|
||||
ticks();
|
||||
result = 1;
|
||||
}
|
||||
|
||||
sigaction(SIGILL,&old_sigill,0);
|
||||
sigaction(SIGFPE,&old_sigfpe,0);
|
||||
sigaction(SIGBUS,&old_sigbus,0);
|
||||
sigaction(SIGSEGV,&old_sigsegv,0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static double osfreq(void)
|
||||
{
|
||||
FILE *f;
|
||||
char *x;
|
||||
double result;
|
||||
int s;
|
||||
|
||||
f = fopen("/etc/cpucyclespersecond", "r");
|
||||
if (f) {
|
||||
s = fscanf(f,"%lf",&result);
|
||||
fclose(f);
|
||||
if (s > 0) return result;
|
||||
}
|
||||
|
||||
f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed", "r");
|
||||
if (f) {
|
||||
s = fscanf(f,"%lf",&result);
|
||||
fclose(f);
|
||||
if (s > 0) return 1000.0 * result;
|
||||
}
|
||||
|
||||
f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "r");
|
||||
if (f) {
|
||||
s = fscanf(f,"%lf",&result);
|
||||
fclose(f);
|
||||
if (s > 0) return 1000.0 * result;
|
||||
}
|
||||
|
||||
f = fopen("/sys/devices/system/cpu/cpu0/clock_tick", "r");
|
||||
if (f) {
|
||||
s = fscanf(f,"%lf",&result);
|
||||
fclose(f);
|
||||
if (s > 0) return result;
|
||||
}
|
||||
|
||||
f = fopen("/proc/cpuinfo","r");
|
||||
if (f) {
|
||||
for (;;) {
|
||||
s = fscanf(f,"cpu MHz : %lf",&result);
|
||||
if (s > 0) break;
|
||||
if (s == 0) s = fscanf(f,"%*[^\n]\n");
|
||||
if (s < 0) { result = 0; break; }
|
||||
}
|
||||
fclose(f);
|
||||
if (result) return 1000000.0 * result;
|
||||
}
|
||||
|
||||
f = fopen("/proc/cpuinfo","r");
|
||||
if (f) {
|
||||
for (;;) {
|
||||
s = fscanf(f,"clock : %lf",&result);
|
||||
if (s > 0) break;
|
||||
if (s == 0) s = fscanf(f,"%*[^\n]\n");
|
||||
if (s < 0) { result = 0; break; }
|
||||
}
|
||||
fclose(f);
|
||||
if (result) return 1000000.0 * result;
|
||||
}
|
||||
|
||||
f = fopen("/proc/cpuinfo","r");
|
||||
if (f) {
|
||||
for (;;) {
|
||||
s = fscanf(f,"cpu MHz static : %lf",&result);
|
||||
if (s > 0) break;
|
||||
if (s == 0) s = fscanf(f,"%*[^\n]\n");
|
||||
if (s < 0) { result = 0; break; }
|
||||
}
|
||||
fclose(f);
|
||||
if (result) return 1000000.0 * result;
|
||||
}
|
||||
|
||||
f = popen("sysctl hw.cpufrequency 2>/dev/null","r");
|
||||
if (f) {
|
||||
s = fscanf(f,"hw.cpufrequency: %lf",&result);
|
||||
pclose(f);
|
||||
if (s > 0) if (result > 0) return result;
|
||||
}
|
||||
|
||||
f = popen("/usr/sbin/lsattr -E -l proc0 -a frequency 2>/dev/null","r");
|
||||
if (f) {
|
||||
s = fscanf(f,"frequency %lf",&result);
|
||||
pclose(f);
|
||||
if (s > 0) return result;
|
||||
}
|
||||
|
||||
f = popen("/usr/sbin/psrinfo -v 2>/dev/null","r");
|
||||
if (f) {
|
||||
for (;;) {
|
||||
s = fscanf(f," The %*s processor operates at %lf MHz",&result);
|
||||
if (s > 0) break;
|
||||
if (s == 0) s = fscanf(f,"%*[^\n]\n");
|
||||
if (s < 0) { result = 0; break; }
|
||||
}
|
||||
pclose(f);
|
||||
if (result) return 1000000.0 * result;
|
||||
}
|
||||
|
||||
x = getenv("cpucyclespersecond");
|
||||
if (x) {
|
||||
s = sscanf(x,"%lf",&result);
|
||||
if (s > 0) return result;
|
||||
}
|
||||
|
||||
return 2399987654.0;
|
||||
}
|
||||
|
||||
static long long persecond = 0;
|
||||
static const char *implementation = "none";
|
||||
|
||||
long long (*cpucycles)(void) = cpucycles_init;
|
||||
|
||||
const char *cpucycles_implementation(void)
|
||||
{
|
||||
cpucycles();
|
||||
return implementation;
|
||||
}
|
||||
|
||||
long long cpucycles_persecond(void)
|
||||
{
|
||||
cpucycles();
|
||||
return persecond;
|
||||
}
|
||||
|
||||
const char *cpucycles_version(void)
|
||||
{
|
||||
return "20230115";
|
||||
}
|
||||
|
||||
// ----- cycle counter scaled from ticks
|
||||
|
||||
static double cpucycles_scaled_scaling = 0;
|
||||
static long long cpucycles_scaled_offset = 0;
|
||||
static long long (*cpucycles_scaled_from)(void) = 0;
|
||||
|
||||
static long long cpucycles_scaled(void)
|
||||
{
|
||||
return (cpucycles_scaled_from()-cpucycles_scaled_offset)*cpucycles_scaled_scaling;
|
||||
}
|
||||
|
||||
// ----- cycle counter extended from 32-bit ticks
|
||||
|
||||
static long long (*cpucycles_extend32_from)(void) = 0;
|
||||
|
||||
static uint32_t cpucycles_extend32_prev_ticks;
|
||||
static long long cpucycles_extend32_prev_us;
|
||||
static long long cpucycles_extend32_prev_cycles;
|
||||
|
||||
static void cpucycles_extend32_setup(void)
|
||||
{
|
||||
long long (*ticks)(void) = cpucycles_extend32_from;
|
||||
cpucycles_extend32_prev_ticks = ticks();
|
||||
cpucycles_extend32_prev_us = cpucycles_microseconds();
|
||||
cpucycles_extend32_prev_cycles = 0;
|
||||
}
|
||||
|
||||
static long long cpucycles_extend32(void)
|
||||
{
|
||||
long long (*ticks)(void) = cpucycles_extend32_from;
|
||||
|
||||
uint32_t new_ticks = ticks();
|
||||
unsigned long long delta_ticks = new_ticks-cpucycles_extend32_prev_ticks;
|
||||
long long new_us = cpucycles_microseconds();
|
||||
long long delta_us = new_us-cpucycles_extend32_prev_us;
|
||||
|
||||
// assume that number of cycles cannot increase by 2^32 in 2ms
|
||||
|
||||
if (delta_us < 1000)
|
||||
return cpucycles_extend32_prev_cycles+delta_ticks;
|
||||
|
||||
cpucycles_extend32_prev_ticks = new_ticks;
|
||||
cpucycles_extend32_prev_us = new_us;
|
||||
|
||||
if (delta_us >= 2000) {
|
||||
long long target = (delta_us*0.000001)*persecond;
|
||||
while (delta_ticks+2147483648ULL < target)
|
||||
delta_ticks += 4294967296ULL;
|
||||
}
|
||||
|
||||
return cpucycles_extend32_prev_cycles += delta_ticks;
|
||||
}
|
||||
|
||||
// ----- estimating cycles per tick
|
||||
|
||||
long long cpucycles_microseconds(void)
|
||||
{
|
||||
struct timeval t;
|
||||
long long result;
|
||||
gettimeofday(&t,(struct timezone *) 0);
|
||||
result = t.tv_sec;
|
||||
result *= 1000000;
|
||||
result += t.tv_usec;
|
||||
return result;
|
||||
}
|
||||
|
||||
static double estimate_cyclespertick(long long (*ticks)(void))
|
||||
{
|
||||
long long t0,t1,us0,us1;
|
||||
|
||||
t0 = ticks();
|
||||
us0 = cpucycles_microseconds();
|
||||
do {
|
||||
t1 = ticks();
|
||||
us1 = cpucycles_microseconds();
|
||||
} while (us1-us0 < 10000 || t1-t0 < 1000);
|
||||
if (t1 <= t0) return 0;
|
||||
t1 -= t0;
|
||||
us1 -= us0;
|
||||
return (persecond * 0.000001 * (double) us1) / (double) t1;
|
||||
}
|
||||
|
||||
// ----- selecting an option
|
||||
|
||||
#include "options.inc"
|
||||
|
||||
#define CALLS 1000
|
||||
#define ESTIMATES 3
|
||||
|
||||
long long cpucycles_init(void)
|
||||
{
|
||||
long long precision[NUMOPTIONS];
|
||||
double scaling[NUMOPTIONS];
|
||||
int only32[NUMOPTIONS];
|
||||
long long bestprecision;
|
||||
long long bestopt;
|
||||
long long opt;
|
||||
|
||||
persecond = osfreq();
|
||||
|
||||
for (opt = 0;opt < NUMOPTIONS;++opt) {
|
||||
long long freq = options[opt].ticks_setup();
|
||||
long long tries;
|
||||
|
||||
precision[opt] = 0;
|
||||
scaling[opt] = 0;
|
||||
only32[opt] = 0;
|
||||
|
||||
if (freq > 0) {
|
||||
scaling[opt] = persecond*1.0/freq;
|
||||
} else if (freq == cpucycles_CYCLECOUNTER) {
|
||||
scaling[opt] = 1.0;
|
||||
} else if (freq == cpucycles_EXTEND32) {
|
||||
only32[opt] = 1;
|
||||
scaling[opt] = 1.0;
|
||||
} else if (freq == cpucycles_MAYBECYCLECOUNTER) {
|
||||
scaling[opt] = 1.0;
|
||||
} else if (freq == cpucycles_FINDMULTIPLIER) {
|
||||
int ok = 0;
|
||||
double denom;
|
||||
long long loop;
|
||||
|
||||
for (denom = 1;denom <= 1024;denom += denom) {
|
||||
double est[ESTIMATES];
|
||||
for (loop = 0;loop < ESTIMATES;++loop)
|
||||
est[loop] = denom*estimate_cyclespertick(options[opt].ticks);
|
||||
scaling[opt] = (double) (long long) est[0];
|
||||
if (scaling[opt] < est[0]-0.5) scaling[opt] += 1;
|
||||
if (scaling[opt] > est[0]+0.5) scaling[opt] -= 1;
|
||||
ok = 1;
|
||||
for (loop = 0;loop < ESTIMATES;++loop) {
|
||||
if (est[loop]-scaling[opt] > 0.1) ok = 0;
|
||||
if (scaling[opt]-est[loop] > 0.1) ok = 0;
|
||||
}
|
||||
if (ok) {
|
||||
scaling[opt] /= denom;
|
||||
break;
|
||||
}
|
||||
scaling[opt] = 0;
|
||||
}
|
||||
if (!ok) continue;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (tries = 0;tries < 10;++tries) {
|
||||
long long t[CALLS+1];
|
||||
long long ok = 1;
|
||||
long long i;
|
||||
|
||||
if (scaling[opt] == 1.0) {
|
||||
for (i = 0;i <= CALLS;++i)
|
||||
t[i] = options[opt].ticks();
|
||||
} else {
|
||||
double scalingopt = scaling[opt];
|
||||
long long offset = options[opt].ticks();
|
||||
for (i = 0;i <= CALLS;++i)
|
||||
t[i] = (options[opt].ticks()-offset)*scalingopt;
|
||||
}
|
||||
for (i = 0;i < CALLS;++i)
|
||||
if (t[i] > t[i+1])
|
||||
ok = 0;
|
||||
if (t[0] == t[CALLS])
|
||||
ok = 0;
|
||||
|
||||
if (ok) {
|
||||
long long smallestdiff = 0;
|
||||
for (i = 0;i < CALLS;++i) {
|
||||
long long diff = t[i+1]-t[i];
|
||||
if (diff <= 0) continue;
|
||||
if (smallestdiff == 0 || diff < smallestdiff)
|
||||
smallestdiff = diff;
|
||||
}
|
||||
precision[opt] = smallestdiff;
|
||||
|
||||
// tilt selection towards more robust counters
|
||||
if (freq != cpucycles_CYCLECOUNTER && freq != cpucycles_EXTEND32)
|
||||
precision[opt] += 100;
|
||||
if (freq > 0)
|
||||
precision[opt] += 100;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise keep trying
|
||||
// since !ok can be caused by overflow
|
||||
// or by core swap
|
||||
}
|
||||
}
|
||||
|
||||
if (tracesetup) {
|
||||
for (opt = 0;opt < NUMOPTIONS;++opt)
|
||||
printf("cpucycles tracesetup %lld %s precision %lld scaling %lf only32 %d\n"
|
||||
,opt,options[opt].implementation,precision[opt],scaling[opt],only32[opt]);
|
||||
}
|
||||
|
||||
bestopt = DEFAULTOPTION;
|
||||
bestprecision = 0;
|
||||
for (opt = 0;opt < NUMOPTIONS;++opt)
|
||||
if (precision[opt] > 0)
|
||||
if (!bestprecision || precision[opt] < bestprecision) {
|
||||
bestopt = opt;
|
||||
bestprecision = precision[opt];
|
||||
}
|
||||
|
||||
implementation = options[bestopt].implementation;
|
||||
|
||||
if (scaling[bestopt] == 1.0) {
|
||||
if (only32[bestopt]) {
|
||||
cpucycles_extend32_from = options[bestopt].ticks;
|
||||
cpucycles_extend32_setup();
|
||||
cpucycles = cpucycles_extend32;
|
||||
} else {
|
||||
cpucycles = options[bestopt].ticks;
|
||||
}
|
||||
} else {
|
||||
cpucycles_scaled_scaling = scaling[bestopt];
|
||||
cpucycles_scaled_from = options[bestopt].ticks;
|
||||
cpucycles_scaled_offset = cpucycles_scaled_from();
|
||||
cpucycles = cpucycles_scaled;
|
||||
}
|
||||
|
||||
return cpucycles();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
return __rdtsc();
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_MAYBECYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// version 20230105
|
||||
// public domain
|
||||
// djb
|
||||
|
||||
#include "cpucycles_internal.h"
|
||||
|
||||
#ifndef __i386__
|
||||
#error this code is only for 32-bit x86 platforms
|
||||
#endif
|
||||
|
||||
long long ticks(void)
|
||||
{
|
||||
long long result;
|
||||
asm volatile(".byte 15;.byte 49" : "=A" (result));
|
||||
return result;
|
||||
}
|
||||
|
||||
long long ticks_setup(void)
|
||||
{
|
||||
if (!cpucycles_works(ticks)) return cpucycles_SKIP;
|
||||
return cpucycles_MAYBECYCLECOUNTER;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
### NAME
|
||||
|
||||
cpucycles - count CPU cycles
|
||||
|
||||
### SYNOPSIS
|
||||
|
||||
#include <cpucycles.h>
|
||||
|
||||
long long count = cpucycles();
|
||||
long long persecond = cpucycles_persecond();
|
||||
const char *implementation = cpucycles_implementation();
|
||||
const char *version = cpucycles_version();
|
||||
|
||||
Link with `-lcpucycles`. Old systems may also need `-lrt`.
|
||||
|
||||
### DESCRIPTION
|
||||
|
||||
`cpucycles()` returns an estimate for the number of CPU cycles that have
|
||||
occurred since an unspecified time in the past (perhaps system boot,
|
||||
perhaps program startup).
|
||||
|
||||
Accessing true cycle counters can be difficult on some CPUs and
|
||||
operating systems. `cpucycles()` does its best to produce accurate
|
||||
results, but selects a low-precision counter if the only other option is
|
||||
failure.
|
||||
|
||||
`cpucycles_persecond()` returns an estimate for the number of CPU cycles
|
||||
per second. This estimate comes from `/etc/cpucyclespersecond` if that
|
||||
file exists, otherwise from various OS mechanisms, otherwise from the
|
||||
`cpucyclespersecond` environment variable if that is set, otherwise
|
||||
2399987654.
|
||||
|
||||
`cpucycles_implementation()` returns the name of the counter in use:
|
||||
e.g., `"amd64-pmc"`.
|
||||
|
||||
`cpucycles_version()` returns the `libcpucycles` version number as a
|
||||
string: e.g., `"20230115"`. Results of `cpucycles_implementation()`
|
||||
should be interpreted relative to `cpucycles_version()`.
|
||||
|
||||
`cpucycles` is actually a function pointer. The first call to
|
||||
`cpucycles()` or `cpucycles_persecond()` or `cpucycles_implementation()`
|
||||
selects one of the available counters and updates the `cpucycles`
|
||||
pointer accordingly. Subsequent calls to `cpucycles()` are thread-safe.
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
**gettimeofday**(2), **clock_gettime**(2)
|
||||
@@ -0,0 +1,447 @@
|
||||
Currently libcpucycles supports the following cycle counters. Some
|
||||
cycle counters are actually other forms of counters that libcpucycles
|
||||
scales to imitate a cycle counter. There is
|
||||
[separate documentation](selection.html)
|
||||
for how libcpucycles makes a choice of cycle counter. See also
|
||||
[security considerations](security.html) regarding enabling or disabling
|
||||
counters and regarding Turbo Boost.
|
||||
|
||||
`amd64-pmc`: Requires a 64-bit Intel/AMD platform. Requires the Linux
|
||||
perf_event interface. Accesses a cycle counter through RDPMC. Requires
|
||||
`/proc/sys/kernel/perf_event_paranoid` to be at most 2 for user-level
|
||||
RDPMC access. This counter runs at the clock frequency of the CPU core.
|
||||
|
||||
`amd64-tsc`, `amd64-tscasm`: Requires a 64-bit Intel/AMD platform.
|
||||
Requires RDTSC to be enabled, which it is by default. Uses RDTSC to
|
||||
access the CPU's time-stamp counter. On current CPUs, this is an
|
||||
off-core clock rather than a cycle counter, but it is typically a very
|
||||
fast off-core clock, making it adequate for seeing cycle counts if
|
||||
overclocking and underclocking are disabled. The difference between
|
||||
`tsc` and `tscasm` is that `tsc` uses the compiler's `__rdtsc()` while
|
||||
`tscasm` uses inline assembly.
|
||||
|
||||
`arm32-cortex`: Requires a 32-bit ARMv7-A platform. Uses
|
||||
`mrc p15, 0, %0, c9, c13, 0` to read the cycle counter. Requires user
|
||||
access to the cycle counter, which is not enabled by default but can be
|
||||
enabled under Linux via
|
||||
[a kernel module](https://github.com/thoughtpolice/enable_arm_pmu).
|
||||
This counter is natively 32 bits, but libcpucycles watches how the
|
||||
counter and `gettimeofday` increase to compute a 64-bit extension of the
|
||||
counter.
|
||||
|
||||
`arm64-pmc`: Requires a 64-bit ARMv8-A platform. Uses
|
||||
`mrs %0, PMCCNTR_EL0` to read the cycle counter. Requires user access
|
||||
to the cycle counter, which is not enabled by default but can be enabled
|
||||
under Linux via
|
||||
[a kernel module](https://github.com/rdolbeau/enable_arm_pmu).
|
||||
|
||||
`arm64-vct`: Requires a 64-bit ARMv8-A platform. Uses
|
||||
`mrs %0, CNTVCT_EL0` to read a "virtual count" timer. This is an
|
||||
off-core clock, typically running at 24MHz. Results are scaled by
|
||||
libcpucycles.
|
||||
|
||||
`mips64-cc`: Requires a 64-bit MIPS platform. (Maybe the same code would
|
||||
also work as `mips32-cc`, but this has not been tested yet.) Uses RDHWR
|
||||
to read the hardware cycle counter (hardware register 2 times a constant
|
||||
scale factor in hardware register 3). This counter is natively 32 bits,
|
||||
but libcpucycles watches how the counter and `gettimeofday` increase to
|
||||
compute a 64-bit extension of the counter.
|
||||
|
||||
`ppc32-mftb`: Requires a 32-bit PowerPC platform. Uses `mftb` and
|
||||
`mftbu` to read the "time base". This is an off-core clock, typically
|
||||
running at 24MHz.
|
||||
|
||||
`ppc64-mftb`: Requires a 64-bit PowerPC platform. Uses `mftb` and
|
||||
`mftbu` to read the "time base". This is an off-core clock, typically
|
||||
running at 24MHz.
|
||||
|
||||
`riscv32-rdcycle`: Requires a 32-bit RISC-V platform. Uses `rdcycle`
|
||||
and `rdcycleh` to read a cycle counter.
|
||||
|
||||
`riscv64-rdcycle`: Requires a 64-bit RISC-V platform. Uses `rdcycle`
|
||||
to read a cycle counter.
|
||||
|
||||
`s390x-stckf`: Requires a 64-bit z/Architecture platform. Uses `stckf`
|
||||
to read the TOD clock, which is documented to run at 4096MHz. On the
|
||||
z15, this looks like a doubling of an off-core 2048MHz clock. Results
|
||||
are scaled by libcpucycles.
|
||||
|
||||
`sparc64-rdtick`: Requires a 64-bit SPARC platform. Uses `rd %tick`
|
||||
to read a cycle counter.
|
||||
|
||||
`x86-tsc`, `x86-tscasm`: Same as `amd64-tsc` and `amd64-tscasm`, but
|
||||
for 32-bit Intel/AMD platforms instead of 64-bit Intel/AMD platforms.
|
||||
|
||||
`default-gettimeofday`: Reasonably portable. Resolution is limited to 1
|
||||
microsecond. Results are scaled by libcpucycles.
|
||||
|
||||
`default-mach`: Requires an OS with `mach_absolute_time()`. Typically
|
||||
runs at 24MHz. Results are scaled by libcpucycles.
|
||||
|
||||
`default-monotonic`: Requires `CLOCK_MONOTONIC`. Reasonably portable,
|
||||
although might fail on older systems where `default-gettimeofday` works.
|
||||
Resolution is limited to 1 nanosecond. Can be almost as good as a cycle
|
||||
counter, or orders of magnitude worse, depending on the OS and CPU.
|
||||
Results are scaled by libcpucycles.
|
||||
|
||||
`default-perfevent`: Requires the Linux `perf_event` interface, and a
|
||||
CPU where `perf_event` supports `PERF_COUNT_HW_CPU_CYCLES`. Similar
|
||||
variations in quality to `default-monotonic`, without the 1-nanosecond
|
||||
limitation.
|
||||
|
||||
`default-zero`: The horrifying last resort if nothing else works.
|
||||
|
||||
## Examples
|
||||
|
||||
These are examples of `cpucycles-info` output on various machines. The
|
||||
machines named `gcc*` are from the
|
||||
[GCC Compile Farm](https://gcc.gnu.org/wiki/CompileFarm).
|
||||
|
||||
A `median` line saying, e.g., `47 +47+28+0+2-5+0+2-5...` means that the
|
||||
differences between adjacent cycle counts were 47+47, 47+28, 47+0, 47+2,
|
||||
47−5, 47+0, 47+2, 47−5, etc., with median difference 47. The first few
|
||||
differences are typically larger because of cache effects.
|
||||
|
||||
`pi3aplus`,
|
||||
Broadcom BCM2837B0:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 arm64-pmc precision 9 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 arm64-vct precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 2 default-perfevent precision 189 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 4 default-monotonic precision 272 scaling 1.400000 only32 0
|
||||
cpucycles tracesetup 5 default-gettimeofday precision 1600 scaling 1400.000000 only32 0
|
||||
cpucycles tracesetup 6 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 1400000000
|
||||
cpucycles implementation arm64-pmc
|
||||
cpucycles median 10 +10+8+3+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 1032000000...4224666667 with 1024 loops 4 microseconds
|
||||
cpucycles observed persecond 1286000000...1756000000 with 2048 loops 7 microseconds
|
||||
cpucycles observed persecond 1368266666...1598000000 with 4096 loops 14 microseconds
|
||||
cpucycles observed persecond 1366700000...1473428572 with 8192 loops 29 microseconds
|
||||
cpucycles observed persecond 1366100000...1417534483 with 16384 loops 59 microseconds
|
||||
cpucycles observed persecond 1332739837...1357132232 with 32768 loops 122 microseconds
|
||||
cpucycles observed persecond 1354483471...1366945834 with 65536 loops 241 microseconds
|
||||
cpucycles observed persecond 1385684989...1392195330 with 131072 loops 472 microseconds
|
||||
cpucycles observed persecond 1347223021...1350328528 with 262144 loops 972 microseconds
|
||||
cpucycles observed persecond 1375460125...1377069853 with 524288 loops 1905 microseconds
|
||||
cpucycles observed persecond 1376527697...1377335961 with 1048576 loops 3808 microseconds
|
||||
```
|
||||
|
||||
`bblack`,
|
||||
TI Sitara XAM3359AZCZ100:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 arm32-cortex precision 8 scaling 1.000000 only32 1
|
||||
cpucycles tracesetup 1 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 1283 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 1200 scaling 1000.000000 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 1000000000
|
||||
cpucycles implementation arm32-cortex
|
||||
cpucycles median 1260 +1506+62+31+7+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+13+7+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 622181818...2101888889 with 1024 loops 10 microseconds
|
||||
cpucycles observed persecond 806133333...1492615385 with 2048 loops 14 microseconds
|
||||
cpucycles observed persecond 879880000...1232565218 with 4096 loops 24 microseconds
|
||||
cpucycles observed persecond 939577777...1130581396 with 8192 loops 44 microseconds
|
||||
cpucycles observed persecond 956954022...1050047059 with 16384 loops 86 microseconds
|
||||
cpucycles observed persecond 982878542...1020685715 with 32768 loops 246 microseconds
|
||||
cpucycles observed persecond 988105105...1012217523 with 65536 loops 332 microseconds
|
||||
cpucycles observed persecond 993752077...1007159723 with 131072 loops 721 microseconds
|
||||
cpucycles observed persecond 995364296...1004009448 with 262144 loops 1377 microseconds
|
||||
cpucycles observed persecond 998216306...1001821536 with 524288 loops 2685 microseconds
|
||||
cpucycles observed persecond 998991848...1000914196 with 1048576 loops 5397 microseconds
|
||||
```
|
||||
|
||||
`hiphop`,
|
||||
Intel Xeon E3-1220 v3:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 amd64-pmc precision 40 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 amd64-tsc precision 124 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 amd64-tscasm precision 124 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-perfevent precision 160 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 4 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 5 default-monotonic precision 272 scaling 3.100000 only32 0
|
||||
cpucycles tracesetup 6 default-gettimeofday precision 3300 scaling 3100.000000 only32 0
|
||||
cpucycles tracesetup 7 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3100000000
|
||||
cpucycles implementation amd64-pmc
|
||||
cpucycles median 44 +38+23+23+23-4+0-4+0-4+0-4+0+10-4-2+1-4+1-4+1+17+1-4+1-4+1-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4
|
||||
cpucycles observed persecond 2066500000...4235000000 with 8192 loops 3 microseconds
|
||||
cpucycles observed persecond 2760833333...4200250000 with 16384 loops 5 microseconds
|
||||
cpucycles observed persecond 2743416666...3313100000 with 32768 loops 11 microseconds
|
||||
cpucycles observed persecond 2986227272...3295000000 with 65536 loops 21 microseconds
|
||||
cpucycles observed persecond 3052069767...3206073171 with 131072 loops 42 microseconds
|
||||
cpucycles observed persecond 3050395348...3125523810 with 262144 loops 85 microseconds
|
||||
cpucycles observed persecond 3085123529...3123059524 with 524288 loops 169 microseconds
|
||||
cpucycles observed persecond 3084561764...3103434912 with 1048576 loops 339 microseconds
|
||||
```
|
||||
|
||||
`nucnuc`,
|
||||
Intel Pentium N3700:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 amd64-pmc precision 26 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 amd64-tsc precision 120 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 amd64-tscasm precision 120 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-perfevent precision 427 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 4 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 5 default-monotonic precision 320 scaling 1.600000 only32 0
|
||||
cpucycles tracesetup 6 default-gettimeofday precision 1800 scaling 1600.000000 only32 0
|
||||
cpucycles tracesetup 7 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 1600000000
|
||||
cpucycles implementation amd64-pmc
|
||||
cpucycles median 66 +12+12+14+14-1-1+0-1+0-1+0-1+0+1-1+0-1+0-1+0-2+0-1+0-1+0-1+0-2+0-1+0-1+0-1+0-2+0-1+0-1+1-1+0-2-1-1+0-1+0-1+0-2+0-1+2+0-1+0-1+0+0-1
|
||||
cpucycles observed persecond 1060500000...2325000000 with 2048 loops 3 microseconds
|
||||
cpucycles observed persecond 1387166666...2208250000 with 4096 loops 5 microseconds
|
||||
cpucycles observed persecond 1376083333...1705500000 with 8192 loops 11 microseconds
|
||||
cpucycles observed persecond 1495727272...1671800000 with 16384 loops 21 microseconds
|
||||
cpucycles observed persecond 1563428571...1655100000 with 32768 loops 41 microseconds
|
||||
cpucycles observed persecond 1580807228...1626234568 with 65536 loops 82 microseconds
|
||||
cpucycles observed persecond 1589539393...1612619632 with 131072 loops 164 microseconds
|
||||
cpucycles observed persecond 1598841463...1610230062 with 262144 loops 327 microseconds
|
||||
cpucycles observed persecond 1564336810...1569988042 with 524288 loops 670 microseconds
|
||||
cpucycles observed persecond 1599759725...1602608098 with 1048576 loops 1310 microseconds
|
||||
```
|
||||
|
||||
`saber214`,
|
||||
AMD FX-8350:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 amd64-pmc precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 1 amd64-tsc precision 167 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 amd64-tscasm precision 168 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 4 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 5 default-monotonic precision 376 scaling 4.013452 only32 0
|
||||
cpucycles tracesetup 6 default-gettimeofday precision 4213 scaling 4013.452000 only32 0
|
||||
cpucycles tracesetup 7 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 4013452000
|
||||
cpucycles implementation amd64-tsc
|
||||
cpucycles median 77 +87-2+21+7+4+1+0+2-2-7-4+0+1+4-2+3+1-2-2+5-6+2+2+2+2+1-1-1+0-4+0-1-1-1-2+3-1-1+2-2+0+0+2+0+0+2-2-2+1-1-2+2-5+2+0+2+0+1+0+3-2-1-1
|
||||
cpucycles observed persecond 2767500000...5759000000 with 4096 loops 3 microseconds
|
||||
cpucycles observed persecond 3426000000...4893800000 with 8192 loops 6 microseconds
|
||||
cpucycles observed persecond 3724076923...4446363637 with 16384 loops 12 microseconds
|
||||
cpucycles observed persecond 3977833333...4363318182 with 32768 loops 23 microseconds
|
||||
cpucycles observed persecond 3984854166...4168739131 with 65536 loops 47 microseconds
|
||||
cpucycles observed persecond 3981709923...4048193799 with 131072 loops 130 microseconds
|
||||
cpucycles observed persecond 3982716417...4026914573 with 262144 loops 200 microseconds
|
||||
cpucycles observed persecond 4001637602...4025136987 with 524288 loops 366 microseconds
|
||||
cpucycles observed persecond 4007411111...4018600248 with 1048576 loops 809 microseconds
|
||||
```
|
||||
|
||||
`gcc14`,
|
||||
Intel Xeon E5-2620 v3,
|
||||
Debian testing (bookworm),
|
||||
Linux kernel 6.0.0-6-amd64:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 amd64-pmc precision 41 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 amd64-tsc precision 148 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 amd64-tscasm precision 148 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-perfevent precision 159 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 4 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 5 default-monotonic precision 289 scaling 3.200000 only32 0
|
||||
cpucycles tracesetup 6 default-gettimeofday precision 3400 scaling 3200.000000 only32 0
|
||||
cpucycles tracesetup 7 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3200000000
|
||||
cpucycles implementation amd64-pmc
|
||||
cpucycles median 47 +47+28+0+2-5+0+2-5+16+2-5+0+2-5+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0
|
||||
cpucycles observed persecond 1653800000...2819333334 with 8192 loops 4 microseconds
|
||||
cpucycles observed persecond 1832111111...2389285715 with 16384 loops 8 microseconds
|
||||
cpucycles observed persecond 1936058823...2207200000 with 32768 loops 16 microseconds
|
||||
cpucycles observed persecond 2052843750...2196200000 with 65536 loops 31 microseconds
|
||||
cpucycles observed persecond 2050750000...2120048388 with 131072 loops 63 microseconds
|
||||
cpucycles observed persecond 2081896825...2117048388 with 262144 loops 125 microseconds
|
||||
cpucycles observed persecond 2089478087...2107044177 with 524288 loops 250 microseconds
|
||||
cpucycles observed persecond 2093343313...2102124249 with 1048576 loops 500 microseconds
|
||||
```
|
||||
|
||||
`gcc23`,
|
||||
Cavium Octeon II V0.1,
|
||||
Debian 8.11,
|
||||
Linux kernel 4.1.4:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 mips64-cc precision 24 scaling 1.000000 only32 1
|
||||
cpucycles tracesetup 1 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 46702 scaling 2.399988 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 45799 scaling 2399.987654 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 2399987654
|
||||
cpucycles implementation mips64-cc
|
||||
cpucycles median 2177 +828+17+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 641900000...1845125000 with 1024 loops 9 microseconds
|
||||
cpucycles observed persecond 745357142...1352083334 with 2048 loops 13 microseconds
|
||||
cpucycles observed persecond 809826086...1162333334 with 4096 loops 22 microseconds
|
||||
cpucycles observed persecond 897717948...1104405406 with 8192 loops 38 microseconds
|
||||
cpucycles observed persecond 957467532...1059986667 with 16384 loops 76 microseconds
|
||||
cpucycles observed persecond 973102189...1029777778 with 32768 loops 136 microseconds
|
||||
cpucycles observed persecond 986518656...1015830828 with 65536 loops 267 microseconds
|
||||
cpucycles observed persecond 993452830...1008166667 with 131072 loops 529 microseconds
|
||||
cpucycles observed persecond 996036966...1003403609 with 262144 loops 1054 microseconds
|
||||
cpucycles observed persecond 984706378...1001682630 with 524288 loops 2131 microseconds
|
||||
cpucycles observed persecond 992585292...1001178580 with 1048576 loops 4296 microseconds
|
||||
```
|
||||
|
||||
`gcc45`,
|
||||
AMD Athlon II X4 640,
|
||||
Debian 8.11,
|
||||
Linux kernel 3.16.0-11-686-pae:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 x86-tsc precision 199 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 x86-tscasm precision 199 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 default-perfevent precision 170 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 4 default-monotonic precision 941 scaling 3.000000 only32 0
|
||||
cpucycles tracesetup 5 default-gettimeofday precision 3200 scaling 3000.000000 only32 0
|
||||
cpucycles tracesetup 6 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3000000000
|
||||
cpucycles implementation default-perfevent
|
||||
cpucycles median 72 +12+0+0+0+0+0+0+0+5+0+0+0+0+0+0+0+2+0+0+0+0+0+0+0+1+0+0+0+0+0+0+0+2+0+0+0+0+0+0+0+1+0+0+0+0+0+0+0+2+0+0+0+0+0+0+0+1+0+0+0+0+0+0
|
||||
cpucycles observed persecond 541500000...1812000000 with 1024 loops 3 microseconds
|
||||
cpucycles observed persecond 712333333...1212250000 with 2048 loops 5 microseconds
|
||||
cpucycles observed persecond 1193285714...1733600000 with 4096 loops 6 microseconds
|
||||
cpucycles observed persecond 1689176470...1804562500 with 8192 loops 33 microseconds
|
||||
cpucycles observed persecond 1713074626...1770600000 with 16384 loops 66 microseconds
|
||||
cpucycles observed persecond 1765107692...1795140625 with 32768 loops 129 microseconds
|
||||
cpucycles observed persecond 1785369649...1800603922 with 65536 loops 256 microseconds
|
||||
cpucycles observed persecond 1781377862...1796288462 with 131072 loops 261 microseconds
|
||||
cpucycles observed persecond 1772647398...1778247827 with 262144 loops 691 microseconds
|
||||
cpucycles observed persecond 1789670493...1794149598 with 524288 loops 870 microseconds
|
||||
cpucycles observed persecond 1860276211...1861561332 with 1048576 loops 3156 microseconds
|
||||
```
|
||||
|
||||
`gcc92`,
|
||||
SiFive Freedom U740,
|
||||
Ubuntu 22.04,
|
||||
Linux kernel 5.15.0-1014-generic:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 riscv64-rdcycle precision 8 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 default-perfevent precision 3024 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 2599 scaling 2.399988 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 2599 scaling 2399.987654 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 2399987654
|
||||
cpucycles implementation riscv64-rdcycle
|
||||
cpucycles median 8 +33+27+1+1+1+1+0+0+0+22+0+0+0+0+0+0+0+628+0+0+0+7+0+0+0+145+0+0+0+0+0+0+0+22+0+0+0+0+0+0+0+158+0+0+0+0+0+0+0+22+0+0+0+0+0+0+0+22+0+0+0+0+0
|
||||
cpucycles observed persecond 530250000...1978000000 with 1024 loops 3 microseconds
|
||||
cpucycles observed persecond 831000000...1915666667 with 2048 loops 4 microseconds
|
||||
cpucycles observed persecond 1055750000...1689500000 with 4096 loops 7 microseconds
|
||||
cpucycles observed persecond 1045562500...1305428572 with 8192 loops 15 microseconds
|
||||
cpucycles observed persecond 1102700000...1236357143 with 16384 loops 29 microseconds
|
||||
cpucycles observed persecond 1176053571...1247444445 with 32768 loops 55 microseconds
|
||||
cpucycles observed persecond 1173321428...1209127273 with 65536 loops 111 microseconds
|
||||
cpucycles observed persecond 1187805429...1205210046 with 131072 loops 220 microseconds
|
||||
cpucycles observed persecond 1192415909...1201157535 with 262144 loops 439 microseconds
|
||||
cpucycles observed persecond 1194694760...1199247717 with 524288 loops 877 microseconds
|
||||
cpucycles observed persecond 1194656004...1197023034 with 1048576 loops 1781 microseconds
|
||||
```
|
||||
|
||||
`gcc103`,
|
||||
Apple M1 (Icestorm-M1 + Firestorm-M1),
|
||||
Debian unstable (bookworm),
|
||||
Linux kernel 6.0.0-rc5-asahi-00001-gc62bd3fe430f:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 arm64-pmc precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 1 arm64-vct precision 186 scaling 86.000000 only32 0
|
||||
cpucycles tracesetup 2 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 4 default-monotonic precision 285 scaling 2.064000 only32 0
|
||||
cpucycles tracesetup 5 default-gettimeofday precision 2264 scaling 2064.000000 only32 0
|
||||
cpucycles tracesetup 6 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 2064000000
|
||||
cpucycles implementation arm64-vct
|
||||
cpucycles median 0 +0+86+0+0+0+0+0+0+0+0+0+0+0+0+86+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+86+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+86+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 1784500000...3655000000 with 8192 loops 3 microseconds
|
||||
cpucycles observed persecond 1773750000...2393666667 with 16384 loops 7 microseconds
|
||||
cpucycles observed persecond 1897733333...2222769231 with 32768 loops 14 microseconds
|
||||
cpucycles observed persecond 1951310344...2114962963 with 65536 loops 28 microseconds
|
||||
cpucycles observed persecond 2024071428...2107000000 with 131072 loops 55 microseconds
|
||||
cpucycles observed persecond 2041531531...2082935780 with 262144 loops 110 microseconds
|
||||
cpucycles observed persecond 2051158371...2071461188 with 524288 loops 220 microseconds
|
||||
cpucycles observed persecond 2058539682...2068309795 with 1048576 loops 440 microseconds
|
||||
```
|
||||
|
||||
`gcc112` (`gcc2-power8`),
|
||||
IBM POWER8E,
|
||||
CentOS 7.9 AltArch,
|
||||
Linux kernel 3.10.0-1127.13.1.el7.ppc64le:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 ppc64-mftb precision 251 scaling 7.207031 only32 0
|
||||
cpucycles tracesetup 1 default-perfevent precision 295 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 536 scaling 3.690000 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 3890 scaling 3690.000000 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3690000000
|
||||
cpucycles implementation ppc64-mftb
|
||||
cpucycles median 195 +2969-8+14+0-8+7-8-7+7+6-7-1+0-1+0+7+7-15+7-1-7+6+0+0-8+0+6+0-8+7+0+7-8-8-7-1+7-8+7+0-8+0+14-8-7+6+0-8+7+7-15+0-1+0-1+14+0-15+14+0-1+7+0
|
||||
cpucycles observed persecond 2603750000...5510000000 with 2048 loops 3 microseconds
|
||||
cpucycles observed persecond 3430500000...6052250000 with 4096 loops 5 microseconds
|
||||
cpucycles observed persecond 3411333333...4457500000 with 8192 loops 11 microseconds
|
||||
cpucycles observed persecond 3548695652...4060333334 with 16384 loops 22 microseconds
|
||||
cpucycles observed persecond 3624977777...3876534884 with 32768 loops 44 microseconds
|
||||
cpucycles observed persecond 3621855555...3745363637 with 65536 loops 89 microseconds
|
||||
cpucycles observed persecond 3660157303...3722227273 with 131072 loops 177 microseconds
|
||||
cpucycles observed persecond 3680471751...3711622160 with 262144 loops 353 microseconds
|
||||
cpucycles observed persecond 3685321074...3700886525 with 524288 loops 706 microseconds
|
||||
cpucycles observed persecond 3687745930...3695537208 with 1048576 loops 1412 microseconds
|
||||
```
|
||||
|
||||
`gcc202`,
|
||||
UltraSparc T5,
|
||||
Debian unstable (bookworm),
|
||||
Linux kernel 5.19.0-2-sparc64-smp:
|
||||
```
|
||||
cpucycles version 20230105
|
||||
cpucycles tracesetup 0 sparc64-rdtick precision 65 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 default-perfevent precision 386 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 442 scaling 3.599910 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 3799 scaling 3599.910000 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3599910000
|
||||
cpucycles implementation sparc64-rdtick
|
||||
cpucycles median 73 +24+0+24+24+24+24+24+24+0+1+24+0+1+24+0+1+24+0+0+1+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+1+0+0+0+0+0+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 2751500000...4258250000 with 4096 loops 5 microseconds
|
||||
cpucycles observed persecond 3289200000...4206875000 with 8192 loops 9 microseconds
|
||||
cpucycles observed persecond 3454789473...3900823530 with 16384 loops 18 microseconds
|
||||
cpucycles observed persecond 3452026315...3659888889 with 32768 loops 37 microseconds
|
||||
cpucycles observed persecond 3543770270...3650916667 with 65536 loops 73 microseconds
|
||||
cpucycles observed persecond 3567299319...3620662069 with 131072 loops 146 microseconds
|
||||
cpucycles observed persecond 3591373287...3618220690 with 262144 loops 291 microseconds
|
||||
cpucycles observed persecond 3597353344...3610774527 with 524288 loops 582 microseconds
|
||||
cpucycles observed persecond 3595899403...3603058071 with 1048576 loops 1172 microseconds
|
||||
```
|
||||
|
||||
IBM z15:
|
||||
```
|
||||
cpucycles version 20230106
|
||||
cpucycles tracesetup 0 s390x-stckf precision 250 scaling 1.269531 only32 0
|
||||
cpucycles tracesetup 1 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 272 scaling 5.200000 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 5400 scaling 5200.000000 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 5200000000
|
||||
cpucycles implementation s390x-stckf
|
||||
cpucycles median 48 +87+8+0-2+0+0+38-2+0+1-3+1+28+0+3-3+1+0+28+0-2+3+0-2+36+0+0+0+1+0+28+0-2+0+3-2+35+1+0-2+0+3+28+0-2+0+0-2+3+25+3+0-2+0+1+35+1+0+0-2+0+28+0
|
||||
cpucycles observed persecond 4948941176...5627733334 with 8192 loops 16 microseconds
|
||||
cpucycles observed persecond 4104125000...5515666667 with 16384 loops 7 microseconds
|
||||
cpucycles observed persecond 5047076923...5987818182 with 32768 loops 12 microseconds
|
||||
cpucycles observed persecond 5044846153...5475708334 with 65536 loops 25 microseconds
|
||||
cpucycles observed persecond 5141313725...5357428572 with 131072 loops 50 microseconds
|
||||
cpucycles observed persecond 5150892156...5257250000 with 262144 loops 101 microseconds
|
||||
cpucycles observed persecond 5183421568...5236549505 with 524288 loops 203 microseconds
|
||||
cpucycles observed persecond 5190282555...5216582717 with 1048576 loops 406 microseconds
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
To download and unpack the latest version of libcpucycles:
|
||||
|
||||
wget -m https://cpucycles.cr.yp.to/libcpucycles-latest-version.txt
|
||||
version=$(cat cpucycles.cr.yp.to/libcpucycles-latest-version.txt)
|
||||
wget -m https://cpucycles.cr.yp.to/libcpucycles-$version.tar.gz
|
||||
tar -xzf cpucycles.cr.yp.to/libcpucycles-$version.tar.gz
|
||||
cd libcpucycles-$version
|
||||
|
||||
Then [install](install.html).
|
||||
|
||||
### Archives and changelog (reverse chronological)
|
||||
|
||||
[`libcpucycles-20230115.tar.gz`](libcpucycles-20230115.tar.gz) [browse](libcpucycles-20230115.html)
|
||||
|
||||
Update actual `cpucycles_version` behavior to match documentation.
|
||||
|
||||
[`libcpucycles-20230110.tar.gz`](libcpucycles-20230110.tar.gz) [browse](libcpucycles-20230110.html)
|
||||
|
||||
`doc/api.md`: Document `cpucycles_version()`.
|
||||
|
||||
Add `s390x-stckf` counter.
|
||||
|
||||
`cpucycles/default-perfevent.c`: Read into `int64_t` instead of `long long`.
|
||||
Add comment explaining issues with `PERF_FORMAT_TOTAL_TIME_RUNNING`.
|
||||
|
||||
`configure`: Improve `uname` handling.
|
||||
|
||||
`doc/api.md`: Update description of default frequency.
|
||||
|
||||
[`libcpucycles-20230105.tar.gz`](libcpucycles-20230105.tar.gz) [browse](libcpucycles-20230105.html)
|
||||
@@ -0,0 +1,91 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
html{overflow-y:scroll}
|
||||
body{font-family:sans-serif}
|
||||
p,ul,ol,blockquote,pre{font-size:0.9em;line-height:1.6em}
|
||||
li p{font-size:1.0em}
|
||||
blockquote p{font-size:1.0em}
|
||||
tt{font-size:1.2em}
|
||||
code{font-size:1.2em}
|
||||
h1{font-size:1.5em}
|
||||
h2{font-size:1.3em}
|
||||
h3{font-size:1.0em}
|
||||
h1 a{text-decoration:none}
|
||||
table{border-collapse:collapse}
|
||||
th,td{border:1px solid black}
|
||||
table a{text-decoration:none}
|
||||
table tr{font-size:0.9em;line-height:1.6em}
|
||||
.links a:hover{text-decoration:underline}
|
||||
.links a:active{text-decoration:underline}
|
||||
.links img{width:200px;padding-left:1em}
|
||||
.links td{border:0px;padding-top:0.5em;padding-bottom:0.5em}
|
||||
.headline{padding:0;font-weight:bold;font-size:1.5em;vertical-align:top;padding-bottom:0.5em;color:#125d0d}
|
||||
.navt{display:inline-block;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;
|
||||
min-width:14%;margin:0;padding:0;padding-left:0.5em;padding-right:0.5em;vertical-align:center;
|
||||
font-weight:bold;font-size:1.1em;text-align:center;border:1px solid black}
|
||||
.here{border-bottom:0px;background-color:#ffffff}
|
||||
.away{background-color:#125d0d;}
|
||||
.away a{text-decoration:none;display:block;color:#ffffff}
|
||||
.away a:hover,.away a:active{text-decoration:underline}
|
||||
.main{margin:0;padding-top:0em;padding-bottom:1%;clear:both}
|
||||
</style>
|
||||
<title>
|
||||
API</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class=headline>
|
||||
libcpucycles
|
||||
</div>
|
||||
<div class=nav>
|
||||
<div class="navt away"><a href=index.html>Intro</a>
|
||||
</div><div class="navt away"><a href=download.html>Download</a>
|
||||
</div><div class="navt away"><a href=install.html>Install</a>
|
||||
</div><div class="navt here">API
|
||||
</div><div class="navt away"><a href=counters.html>Counters</a>
|
||||
</div><div class="navt away"><a href=selection.html>Selection</a>
|
||||
</div><div class="navt away"><a href=security.html>Security</a>
|
||||
</div></div>
|
||||
<div class=main>
|
||||
<h3>NAME</h3>
|
||||
<p>cpucycles - count CPU cycles</p>
|
||||
<h3>SYNOPSIS</h3>
|
||||
<pre><code>#include <cpucycles.h>
|
||||
|
||||
long long count = cpucycles();
|
||||
long long persecond = cpucycles_persecond();
|
||||
const char *implementation = cpucycles_implementation();
|
||||
const char *version = cpucycles_version();
|
||||
</code></pre>
|
||||
<p>Link with <code>-lcpucycles</code>. Old systems may also need <code>-lrt</code>.</p>
|
||||
<h3>DESCRIPTION</h3>
|
||||
<p><code>cpucycles()</code> returns an estimate for the number of CPU cycles that have
|
||||
occurred since an unspecified time in the past (perhaps system boot,
|
||||
perhaps program startup).</p>
|
||||
<p>Accessing true cycle counters can be difficult on some CPUs and
|
||||
operating systems. <code>cpucycles()</code> does its best to produce accurate
|
||||
results, but selects a low-precision counter if the only other option is
|
||||
failure.</p>
|
||||
<p><code>cpucycles_persecond()</code> returns an estimate for the number of CPU cycles
|
||||
per second. This estimate comes from <code>/etc/cpucyclespersecond</code> if that
|
||||
file exists, otherwise from various OS mechanisms, otherwise from the
|
||||
<code>cpucyclespersecond</code> environment variable if that is set, otherwise
|
||||
2399987654.</p>
|
||||
<p><code>cpucycles_implementation()</code> returns the name of the counter in use:
|
||||
e.g., <code>"amd64-pmc"</code>.</p>
|
||||
<p><code>cpucycles_version()</code> returns the <code>libcpucycles</code> version number as a
|
||||
string: e.g., <code>"20230115"</code>. Results of <code>cpucycles_implementation()</code>
|
||||
should be interpreted relative to <code>cpucycles_version()</code>.</p>
|
||||
<p><code>cpucycles</code> is actually a function pointer. The first call to
|
||||
<code>cpucycles()</code> or <code>cpucycles_persecond()</code> or <code>cpucycles_implementation()</code>
|
||||
selects one of the available counters and updates the <code>cpucycles</code>
|
||||
pointer accordingly. Subsequent calls to <code>cpucycles()</code> are thread-safe.</p>
|
||||
<h3>SEE ALSO</h3>
|
||||
<p><strong>gettimeofday</strong>(2), <strong>clock_gettime</strong>(2)</p><hr><font size=1><b>Version:</b>
|
||||
This is version 2023.01.15 of the "API" web page.
|
||||
</font>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,456 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
html{overflow-y:scroll}
|
||||
body{font-family:sans-serif}
|
||||
p,ul,ol,blockquote,pre{font-size:0.9em;line-height:1.6em}
|
||||
li p{font-size:1.0em}
|
||||
blockquote p{font-size:1.0em}
|
||||
tt{font-size:1.2em}
|
||||
code{font-size:1.2em}
|
||||
h1{font-size:1.5em}
|
||||
h2{font-size:1.3em}
|
||||
h3{font-size:1.0em}
|
||||
h1 a{text-decoration:none}
|
||||
table{border-collapse:collapse}
|
||||
th,td{border:1px solid black}
|
||||
table a{text-decoration:none}
|
||||
table tr{font-size:0.9em;line-height:1.6em}
|
||||
.links a:hover{text-decoration:underline}
|
||||
.links a:active{text-decoration:underline}
|
||||
.links img{width:200px;padding-left:1em}
|
||||
.links td{border:0px;padding-top:0.5em;padding-bottom:0.5em}
|
||||
.headline{padding:0;font-weight:bold;font-size:1.5em;vertical-align:top;padding-bottom:0.5em;color:#125d0d}
|
||||
.navt{display:inline-block;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;
|
||||
min-width:14%;margin:0;padding:0;padding-left:0.5em;padding-right:0.5em;vertical-align:center;
|
||||
font-weight:bold;font-size:1.1em;text-align:center;border:1px solid black}
|
||||
.here{border-bottom:0px;background-color:#ffffff}
|
||||
.away{background-color:#125d0d;}
|
||||
.away a{text-decoration:none;display:block;color:#ffffff}
|
||||
.away a:hover,.away a:active{text-decoration:underline}
|
||||
.main{margin:0;padding-top:0em;padding-bottom:1%;clear:both}
|
||||
</style>
|
||||
<title>
|
||||
Counters</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class=headline>
|
||||
libcpucycles
|
||||
</div>
|
||||
<div class=nav>
|
||||
<div class="navt away"><a href=index.html>Intro</a>
|
||||
</div><div class="navt away"><a href=download.html>Download</a>
|
||||
</div><div class="navt away"><a href=install.html>Install</a>
|
||||
</div><div class="navt away"><a href=api.html>API</a>
|
||||
</div><div class="navt here">Counters
|
||||
</div><div class="navt away"><a href=selection.html>Selection</a>
|
||||
</div><div class="navt away"><a href=security.html>Security</a>
|
||||
</div></div>
|
||||
<div class=main>
|
||||
<p>Currently libcpucycles supports the following cycle counters. Some
|
||||
cycle counters are actually other forms of counters that libcpucycles
|
||||
scales to imitate a cycle counter. There is
|
||||
<a href="selection.html">separate documentation</a>
|
||||
for how libcpucycles makes a choice of cycle counter. See also
|
||||
<a href="security.html">security considerations</a> regarding enabling or disabling
|
||||
counters and regarding Turbo Boost.</p>
|
||||
<p><code>amd64-pmc</code>: Requires a 64-bit Intel/AMD platform. Requires the Linux
|
||||
perf_event interface. Accesses a cycle counter through RDPMC. Requires
|
||||
<code>/proc/sys/kernel/perf_event_paranoid</code> to be at most 2 for user-level
|
||||
RDPMC access. This counter runs at the clock frequency of the CPU core.</p>
|
||||
<p><code>amd64-tsc</code>, <code>amd64-tscasm</code>: Requires a 64-bit Intel/AMD platform.
|
||||
Requires RDTSC to be enabled, which it is by default. Uses RDTSC to
|
||||
access the CPU's time-stamp counter. On current CPUs, this is an
|
||||
off-core clock rather than a cycle counter, but it is typically a very
|
||||
fast off-core clock, making it adequate for seeing cycle counts if
|
||||
overclocking and underclocking are disabled. The difference between
|
||||
<code>tsc</code> and <code>tscasm</code> is that <code>tsc</code> uses the compiler's <code>__rdtsc()</code> while
|
||||
<code>tscasm</code> uses inline assembly.</p>
|
||||
<p><code>arm32-cortex</code>: Requires a 32-bit ARMv7-A platform. Uses
|
||||
<code>mrc p15, 0, %0, c9, c13, 0</code> to read the cycle counter. Requires user
|
||||
access to the cycle counter, which is not enabled by default but can be
|
||||
enabled under Linux via
|
||||
<a href="https://github.com/thoughtpolice/enable_arm_pmu">a kernel module</a>.
|
||||
This counter is natively 32 bits, but libcpucycles watches how the
|
||||
counter and <code>gettimeofday</code> increase to compute a 64-bit extension of the
|
||||
counter.</p>
|
||||
<p><code>arm64-pmc</code>: Requires a 64-bit ARMv8-A platform. Uses
|
||||
<code>mrs %0, PMCCNTR_EL0</code> to read the cycle counter. Requires user access
|
||||
to the cycle counter, which is not enabled by default but can be enabled
|
||||
under Linux via
|
||||
<a href="https://github.com/rdolbeau/enable_arm_pmu">a kernel module</a>.</p>
|
||||
<p><code>arm64-vct</code>: Requires a 64-bit ARMv8-A platform. Uses
|
||||
<code>mrs %0, CNTVCT_EL0</code> to read a "virtual count" timer. This is an
|
||||
off-core clock, typically running at 24MHz. Results are scaled by
|
||||
libcpucycles.</p>
|
||||
<p><code>mips64-cc</code>: Requires a 64-bit MIPS platform. (Maybe the same code would
|
||||
also work as <code>mips32-cc</code>, but this has not been tested yet.) Uses RDHWR
|
||||
to read the hardware cycle counter (hardware register 2 times a constant
|
||||
scale factor in hardware register 3). This counter is natively 32 bits,
|
||||
but libcpucycles watches how the counter and <code>gettimeofday</code> increase to
|
||||
compute a 64-bit extension of the counter.</p>
|
||||
<p><code>ppc32-mftb</code>: Requires a 32-bit PowerPC platform. Uses <code>mftb</code> and
|
||||
<code>mftbu</code> to read the "time base". This is an off-core clock, typically
|
||||
running at 24MHz.</p>
|
||||
<p><code>ppc64-mftb</code>: Requires a 64-bit PowerPC platform. Uses <code>mftb</code> and
|
||||
<code>mftbu</code> to read the "time base". This is an off-core clock, typically
|
||||
running at 24MHz.</p>
|
||||
<p><code>riscv32-rdcycle</code>: Requires a 32-bit RISC-V platform. Uses <code>rdcycle</code>
|
||||
and <code>rdcycleh</code> to read a cycle counter.</p>
|
||||
<p><code>riscv64-rdcycle</code>: Requires a 64-bit RISC-V platform. Uses <code>rdcycle</code>
|
||||
to read a cycle counter.</p>
|
||||
<p><code>s390x-stckf</code>: Requires a 64-bit z/Architecture platform. Uses <code>stckf</code>
|
||||
to read the TOD clock, which is documented to run at 4096MHz. On the
|
||||
z15, this looks like a doubling of an off-core 2048MHz clock. Results
|
||||
are scaled by libcpucycles.</p>
|
||||
<p><code>sparc64-rdtick</code>: Requires a 64-bit SPARC platform. Uses <code>rd %tick</code>
|
||||
to read a cycle counter.</p>
|
||||
<p><code>x86-tsc</code>, <code>x86-tscasm</code>: Same as <code>amd64-tsc</code> and <code>amd64-tscasm</code>, but
|
||||
for 32-bit Intel/AMD platforms instead of 64-bit Intel/AMD platforms.</p>
|
||||
<p><code>default-gettimeofday</code>: Reasonably portable. Resolution is limited to 1
|
||||
microsecond. Results are scaled by libcpucycles.</p>
|
||||
<p><code>default-mach</code>: Requires an OS with <code>mach_absolute_time()</code>. Typically
|
||||
runs at 24MHz. Results are scaled by libcpucycles.</p>
|
||||
<p><code>default-monotonic</code>: Requires <code>CLOCK_MONOTONIC</code>. Reasonably portable,
|
||||
although might fail on older systems where <code>default-gettimeofday</code> works.
|
||||
Resolution is limited to 1 nanosecond. Can be almost as good as a cycle
|
||||
counter, or orders of magnitude worse, depending on the OS and CPU.
|
||||
Results are scaled by libcpucycles.</p>
|
||||
<p><code>default-perfevent</code>: Requires the Linux <code>perf_event</code> interface, and a
|
||||
CPU where <code>perf_event</code> supports <code>PERF_COUNT_HW_CPU_CYCLES</code>. Similar
|
||||
variations in quality to <code>default-monotonic</code>, without the 1-nanosecond
|
||||
limitation.</p>
|
||||
<p><code>default-zero</code>: The horrifying last resort if nothing else works.</p>
|
||||
<h2>Examples</h2>
|
||||
<p>These are examples of <code>cpucycles-info</code> output on various machines. The
|
||||
machines named <code>gcc*</code> are from the
|
||||
<a href="https://gcc.gnu.org/wiki/CompileFarm">GCC Compile Farm</a>.</p>
|
||||
<p>A <code>median</code> line saying, e.g., <code>47 +47+28+0+2-5+0+2-5...</code> means that the
|
||||
differences between adjacent cycle counts were 47+47, 47+28, 47+0, 47+2,
|
||||
47−5, 47+0, 47+2, 47−5, etc., with median difference 47. The first few
|
||||
differences are typically larger because of cache effects.</p>
|
||||
<p><code>pi3aplus</code>,
|
||||
Broadcom BCM2837B0:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 arm64-pmc precision 9 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 arm64-vct precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 2 default-perfevent precision 189 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 4 default-monotonic precision 272 scaling 1.400000 only32 0
|
||||
cpucycles tracesetup 5 default-gettimeofday precision 1600 scaling 1400.000000 only32 0
|
||||
cpucycles tracesetup 6 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 1400000000
|
||||
cpucycles implementation arm64-pmc
|
||||
cpucycles median 10 +10+8+3+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 1032000000...4224666667 with 1024 loops 4 microseconds
|
||||
cpucycles observed persecond 1286000000...1756000000 with 2048 loops 7 microseconds
|
||||
cpucycles observed persecond 1368266666...1598000000 with 4096 loops 14 microseconds
|
||||
cpucycles observed persecond 1366700000...1473428572 with 8192 loops 29 microseconds
|
||||
cpucycles observed persecond 1366100000...1417534483 with 16384 loops 59 microseconds
|
||||
cpucycles observed persecond 1332739837...1357132232 with 32768 loops 122 microseconds
|
||||
cpucycles observed persecond 1354483471...1366945834 with 65536 loops 241 microseconds
|
||||
cpucycles observed persecond 1385684989...1392195330 with 131072 loops 472 microseconds
|
||||
cpucycles observed persecond 1347223021...1350328528 with 262144 loops 972 microseconds
|
||||
cpucycles observed persecond 1375460125...1377069853 with 524288 loops 1905 microseconds
|
||||
cpucycles observed persecond 1376527697...1377335961 with 1048576 loops 3808 microseconds
|
||||
</code></pre>
|
||||
<p><code>bblack</code>,
|
||||
TI Sitara XAM3359AZCZ100:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 arm32-cortex precision 8 scaling 1.000000 only32 1
|
||||
cpucycles tracesetup 1 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 1283 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 1200 scaling 1000.000000 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 1000000000
|
||||
cpucycles implementation arm32-cortex
|
||||
cpucycles median 1260 +1506+62+31+7+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+13+7+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 622181818...2101888889 with 1024 loops 10 microseconds
|
||||
cpucycles observed persecond 806133333...1492615385 with 2048 loops 14 microseconds
|
||||
cpucycles observed persecond 879880000...1232565218 with 4096 loops 24 microseconds
|
||||
cpucycles observed persecond 939577777...1130581396 with 8192 loops 44 microseconds
|
||||
cpucycles observed persecond 956954022...1050047059 with 16384 loops 86 microseconds
|
||||
cpucycles observed persecond 982878542...1020685715 with 32768 loops 246 microseconds
|
||||
cpucycles observed persecond 988105105...1012217523 with 65536 loops 332 microseconds
|
||||
cpucycles observed persecond 993752077...1007159723 with 131072 loops 721 microseconds
|
||||
cpucycles observed persecond 995364296...1004009448 with 262144 loops 1377 microseconds
|
||||
cpucycles observed persecond 998216306...1001821536 with 524288 loops 2685 microseconds
|
||||
cpucycles observed persecond 998991848...1000914196 with 1048576 loops 5397 microseconds
|
||||
</code></pre>
|
||||
<p><code>hiphop</code>,
|
||||
Intel Xeon E3-1220 v3:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 amd64-pmc precision 40 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 amd64-tsc precision 124 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 amd64-tscasm precision 124 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-perfevent precision 160 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 4 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 5 default-monotonic precision 272 scaling 3.100000 only32 0
|
||||
cpucycles tracesetup 6 default-gettimeofday precision 3300 scaling 3100.000000 only32 0
|
||||
cpucycles tracesetup 7 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3100000000
|
||||
cpucycles implementation amd64-pmc
|
||||
cpucycles median 44 +38+23+23+23-4+0-4+0-4+0-4+0+10-4-2+1-4+1-4+1+17+1-4+1-4+1-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4+0-4
|
||||
cpucycles observed persecond 2066500000...4235000000 with 8192 loops 3 microseconds
|
||||
cpucycles observed persecond 2760833333...4200250000 with 16384 loops 5 microseconds
|
||||
cpucycles observed persecond 2743416666...3313100000 with 32768 loops 11 microseconds
|
||||
cpucycles observed persecond 2986227272...3295000000 with 65536 loops 21 microseconds
|
||||
cpucycles observed persecond 3052069767...3206073171 with 131072 loops 42 microseconds
|
||||
cpucycles observed persecond 3050395348...3125523810 with 262144 loops 85 microseconds
|
||||
cpucycles observed persecond 3085123529...3123059524 with 524288 loops 169 microseconds
|
||||
cpucycles observed persecond 3084561764...3103434912 with 1048576 loops 339 microseconds
|
||||
</code></pre>
|
||||
<p><code>nucnuc</code>,
|
||||
Intel Pentium N3700:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 amd64-pmc precision 26 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 amd64-tsc precision 120 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 amd64-tscasm precision 120 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-perfevent precision 427 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 4 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 5 default-monotonic precision 320 scaling 1.600000 only32 0
|
||||
cpucycles tracesetup 6 default-gettimeofday precision 1800 scaling 1600.000000 only32 0
|
||||
cpucycles tracesetup 7 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 1600000000
|
||||
cpucycles implementation amd64-pmc
|
||||
cpucycles median 66 +12+12+14+14-1-1+0-1+0-1+0-1+0+1-1+0-1+0-1+0-2+0-1+0-1+0-1+0-2+0-1+0-1+0-1+0-2+0-1+0-1+1-1+0-2-1-1+0-1+0-1+0-2+0-1+2+0-1+0-1+0+0-1
|
||||
cpucycles observed persecond 1060500000...2325000000 with 2048 loops 3 microseconds
|
||||
cpucycles observed persecond 1387166666...2208250000 with 4096 loops 5 microseconds
|
||||
cpucycles observed persecond 1376083333...1705500000 with 8192 loops 11 microseconds
|
||||
cpucycles observed persecond 1495727272...1671800000 with 16384 loops 21 microseconds
|
||||
cpucycles observed persecond 1563428571...1655100000 with 32768 loops 41 microseconds
|
||||
cpucycles observed persecond 1580807228...1626234568 with 65536 loops 82 microseconds
|
||||
cpucycles observed persecond 1589539393...1612619632 with 131072 loops 164 microseconds
|
||||
cpucycles observed persecond 1598841463...1610230062 with 262144 loops 327 microseconds
|
||||
cpucycles observed persecond 1564336810...1569988042 with 524288 loops 670 microseconds
|
||||
cpucycles observed persecond 1599759725...1602608098 with 1048576 loops 1310 microseconds
|
||||
</code></pre>
|
||||
<p><code>saber214</code>,
|
||||
AMD FX-8350:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 amd64-pmc precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 1 amd64-tsc precision 167 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 amd64-tscasm precision 168 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 4 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 5 default-monotonic precision 376 scaling 4.013452 only32 0
|
||||
cpucycles tracesetup 6 default-gettimeofday precision 4213 scaling 4013.452000 only32 0
|
||||
cpucycles tracesetup 7 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 4013452000
|
||||
cpucycles implementation amd64-tsc
|
||||
cpucycles median 77 +87-2+21+7+4+1+0+2-2-7-4+0+1+4-2+3+1-2-2+5-6+2+2+2+2+1-1-1+0-4+0-1-1-1-2+3-1-1+2-2+0+0+2+0+0+2-2-2+1-1-2+2-5+2+0+2+0+1+0+3-2-1-1
|
||||
cpucycles observed persecond 2767500000...5759000000 with 4096 loops 3 microseconds
|
||||
cpucycles observed persecond 3426000000...4893800000 with 8192 loops 6 microseconds
|
||||
cpucycles observed persecond 3724076923...4446363637 with 16384 loops 12 microseconds
|
||||
cpucycles observed persecond 3977833333...4363318182 with 32768 loops 23 microseconds
|
||||
cpucycles observed persecond 3984854166...4168739131 with 65536 loops 47 microseconds
|
||||
cpucycles observed persecond 3981709923...4048193799 with 131072 loops 130 microseconds
|
||||
cpucycles observed persecond 3982716417...4026914573 with 262144 loops 200 microseconds
|
||||
cpucycles observed persecond 4001637602...4025136987 with 524288 loops 366 microseconds
|
||||
cpucycles observed persecond 4007411111...4018600248 with 1048576 loops 809 microseconds
|
||||
</code></pre>
|
||||
<p><code>gcc14</code>,
|
||||
Intel Xeon E5-2620 v3,
|
||||
Debian testing (bookworm),
|
||||
Linux kernel 6.0.0-6-amd64:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 amd64-pmc precision 41 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 amd64-tsc precision 148 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 amd64-tscasm precision 148 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-perfevent precision 159 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 4 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 5 default-monotonic precision 289 scaling 3.200000 only32 0
|
||||
cpucycles tracesetup 6 default-gettimeofday precision 3400 scaling 3200.000000 only32 0
|
||||
cpucycles tracesetup 7 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3200000000
|
||||
cpucycles implementation amd64-pmc
|
||||
cpucycles median 47 +47+28+0+2-5+0+2-5+16+2-5+0+2-5+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0+1-4+0
|
||||
cpucycles observed persecond 1653800000...2819333334 with 8192 loops 4 microseconds
|
||||
cpucycles observed persecond 1832111111...2389285715 with 16384 loops 8 microseconds
|
||||
cpucycles observed persecond 1936058823...2207200000 with 32768 loops 16 microseconds
|
||||
cpucycles observed persecond 2052843750...2196200000 with 65536 loops 31 microseconds
|
||||
cpucycles observed persecond 2050750000...2120048388 with 131072 loops 63 microseconds
|
||||
cpucycles observed persecond 2081896825...2117048388 with 262144 loops 125 microseconds
|
||||
cpucycles observed persecond 2089478087...2107044177 with 524288 loops 250 microseconds
|
||||
cpucycles observed persecond 2093343313...2102124249 with 1048576 loops 500 microseconds
|
||||
</code></pre>
|
||||
<p><code>gcc23</code>,
|
||||
Cavium Octeon II V0.1,
|
||||
Debian 8.11,
|
||||
Linux kernel 4.1.4:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 mips64-cc precision 24 scaling 1.000000 only32 1
|
||||
cpucycles tracesetup 1 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 46702 scaling 2.399988 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 45799 scaling 2399.987654 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 2399987654
|
||||
cpucycles implementation mips64-cc
|
||||
cpucycles median 2177 +828+17+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 641900000...1845125000 with 1024 loops 9 microseconds
|
||||
cpucycles observed persecond 745357142...1352083334 with 2048 loops 13 microseconds
|
||||
cpucycles observed persecond 809826086...1162333334 with 4096 loops 22 microseconds
|
||||
cpucycles observed persecond 897717948...1104405406 with 8192 loops 38 microseconds
|
||||
cpucycles observed persecond 957467532...1059986667 with 16384 loops 76 microseconds
|
||||
cpucycles observed persecond 973102189...1029777778 with 32768 loops 136 microseconds
|
||||
cpucycles observed persecond 986518656...1015830828 with 65536 loops 267 microseconds
|
||||
cpucycles observed persecond 993452830...1008166667 with 131072 loops 529 microseconds
|
||||
cpucycles observed persecond 996036966...1003403609 with 262144 loops 1054 microseconds
|
||||
cpucycles observed persecond 984706378...1001682630 with 524288 loops 2131 microseconds
|
||||
cpucycles observed persecond 992585292...1001178580 with 1048576 loops 4296 microseconds
|
||||
</code></pre>
|
||||
<p><code>gcc45</code>,
|
||||
AMD Athlon II X4 640,
|
||||
Debian 8.11,
|
||||
Linux kernel 3.16.0-11-686-pae:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 x86-tsc precision 199 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 x86-tscasm precision 199 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 default-perfevent precision 170 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 3 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 4 default-monotonic precision 941 scaling 3.000000 only32 0
|
||||
cpucycles tracesetup 5 default-gettimeofday precision 3200 scaling 3000.000000 only32 0
|
||||
cpucycles tracesetup 6 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3000000000
|
||||
cpucycles implementation default-perfevent
|
||||
cpucycles median 72 +12+0+0+0+0+0+0+0+5+0+0+0+0+0+0+0+2+0+0+0+0+0+0+0+1+0+0+0+0+0+0+0+2+0+0+0+0+0+0+0+1+0+0+0+0+0+0+0+2+0+0+0+0+0+0+0+1+0+0+0+0+0+0
|
||||
cpucycles observed persecond 541500000...1812000000 with 1024 loops 3 microseconds
|
||||
cpucycles observed persecond 712333333...1212250000 with 2048 loops 5 microseconds
|
||||
cpucycles observed persecond 1193285714...1733600000 with 4096 loops 6 microseconds
|
||||
cpucycles observed persecond 1689176470...1804562500 with 8192 loops 33 microseconds
|
||||
cpucycles observed persecond 1713074626...1770600000 with 16384 loops 66 microseconds
|
||||
cpucycles observed persecond 1765107692...1795140625 with 32768 loops 129 microseconds
|
||||
cpucycles observed persecond 1785369649...1800603922 with 65536 loops 256 microseconds
|
||||
cpucycles observed persecond 1781377862...1796288462 with 131072 loops 261 microseconds
|
||||
cpucycles observed persecond 1772647398...1778247827 with 262144 loops 691 microseconds
|
||||
cpucycles observed persecond 1789670493...1794149598 with 524288 loops 870 microseconds
|
||||
cpucycles observed persecond 1860276211...1861561332 with 1048576 loops 3156 microseconds
|
||||
</code></pre>
|
||||
<p><code>gcc92</code>,
|
||||
SiFive Freedom U740,
|
||||
Ubuntu 22.04,
|
||||
Linux kernel 5.15.0-1014-generic:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 riscv64-rdcycle precision 8 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 default-perfevent precision 3024 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 2599 scaling 2.399988 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 2599 scaling 2399.987654 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 2399987654
|
||||
cpucycles implementation riscv64-rdcycle
|
||||
cpucycles median 8 +33+27+1+1+1+1+0+0+0+22+0+0+0+0+0+0+0+628+0+0+0+7+0+0+0+145+0+0+0+0+0+0+0+22+0+0+0+0+0+0+0+158+0+0+0+0+0+0+0+22+0+0+0+0+0+0+0+22+0+0+0+0+0
|
||||
cpucycles observed persecond 530250000...1978000000 with 1024 loops 3 microseconds
|
||||
cpucycles observed persecond 831000000...1915666667 with 2048 loops 4 microseconds
|
||||
cpucycles observed persecond 1055750000...1689500000 with 4096 loops 7 microseconds
|
||||
cpucycles observed persecond 1045562500...1305428572 with 8192 loops 15 microseconds
|
||||
cpucycles observed persecond 1102700000...1236357143 with 16384 loops 29 microseconds
|
||||
cpucycles observed persecond 1176053571...1247444445 with 32768 loops 55 microseconds
|
||||
cpucycles observed persecond 1173321428...1209127273 with 65536 loops 111 microseconds
|
||||
cpucycles observed persecond 1187805429...1205210046 with 131072 loops 220 microseconds
|
||||
cpucycles observed persecond 1192415909...1201157535 with 262144 loops 439 microseconds
|
||||
cpucycles observed persecond 1194694760...1199247717 with 524288 loops 877 microseconds
|
||||
cpucycles observed persecond 1194656004...1197023034 with 1048576 loops 1781 microseconds
|
||||
</code></pre>
|
||||
<p><code>gcc103</code>,
|
||||
Apple M1 (Icestorm-M1 + Firestorm-M1),
|
||||
Debian unstable (bookworm),
|
||||
Linux kernel 6.0.0-rc5-asahi-00001-gc62bd3fe430f:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 arm64-pmc precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 1 arm64-vct precision 186 scaling 86.000000 only32 0
|
||||
cpucycles tracesetup 2 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 4 default-monotonic precision 285 scaling 2.064000 only32 0
|
||||
cpucycles tracesetup 5 default-gettimeofday precision 2264 scaling 2064.000000 only32 0
|
||||
cpucycles tracesetup 6 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 2064000000
|
||||
cpucycles implementation arm64-vct
|
||||
cpucycles median 0 +0+86+0+0+0+0+0+0+0+0+0+0+0+0+86+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+86+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+86+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 1784500000...3655000000 with 8192 loops 3 microseconds
|
||||
cpucycles observed persecond 1773750000...2393666667 with 16384 loops 7 microseconds
|
||||
cpucycles observed persecond 1897733333...2222769231 with 32768 loops 14 microseconds
|
||||
cpucycles observed persecond 1951310344...2114962963 with 65536 loops 28 microseconds
|
||||
cpucycles observed persecond 2024071428...2107000000 with 131072 loops 55 microseconds
|
||||
cpucycles observed persecond 2041531531...2082935780 with 262144 loops 110 microseconds
|
||||
cpucycles observed persecond 2051158371...2071461188 with 524288 loops 220 microseconds
|
||||
cpucycles observed persecond 2058539682...2068309795 with 1048576 loops 440 microseconds
|
||||
</code></pre>
|
||||
<p><code>gcc112</code> (<code>gcc2-power8</code>),
|
||||
IBM POWER8E,
|
||||
CentOS 7.9 AltArch,
|
||||
Linux kernel 3.10.0-1127.13.1.el7.ppc64le:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 ppc64-mftb precision 251 scaling 7.207031 only32 0
|
||||
cpucycles tracesetup 1 default-perfevent precision 295 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 536 scaling 3.690000 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 3890 scaling 3690.000000 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3690000000
|
||||
cpucycles implementation ppc64-mftb
|
||||
cpucycles median 195 +2969-8+14+0-8+7-8-7+7+6-7-1+0-1+0+7+7-15+7-1-7+6+0+0-8+0+6+0-8+7+0+7-8-8-7-1+7-8+7+0-8+0+14-8-7+6+0-8+7+7-15+0-1+0-1+14+0-15+14+0-1+7+0
|
||||
cpucycles observed persecond 2603750000...5510000000 with 2048 loops 3 microseconds
|
||||
cpucycles observed persecond 3430500000...6052250000 with 4096 loops 5 microseconds
|
||||
cpucycles observed persecond 3411333333...4457500000 with 8192 loops 11 microseconds
|
||||
cpucycles observed persecond 3548695652...4060333334 with 16384 loops 22 microseconds
|
||||
cpucycles observed persecond 3624977777...3876534884 with 32768 loops 44 microseconds
|
||||
cpucycles observed persecond 3621855555...3745363637 with 65536 loops 89 microseconds
|
||||
cpucycles observed persecond 3660157303...3722227273 with 131072 loops 177 microseconds
|
||||
cpucycles observed persecond 3680471751...3711622160 with 262144 loops 353 microseconds
|
||||
cpucycles observed persecond 3685321074...3700886525 with 524288 loops 706 microseconds
|
||||
cpucycles observed persecond 3687745930...3695537208 with 1048576 loops 1412 microseconds
|
||||
</code></pre>
|
||||
<p><code>gcc202</code>,
|
||||
UltraSparc T5,
|
||||
Debian unstable (bookworm),
|
||||
Linux kernel 5.19.0-2-sparc64-smp:</p>
|
||||
<pre><code>cpucycles version 20230105
|
||||
cpucycles tracesetup 0 sparc64-rdtick precision 65 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 1 default-perfevent precision 386 scaling 1.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 442 scaling 3.599910 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 3799 scaling 3599.910000 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 3599910000
|
||||
cpucycles implementation sparc64-rdtick
|
||||
cpucycles median 73 +24+0+24+24+24+24+24+24+0+1+24+0+1+24+0+1+24+0+0+1+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+1+0+0+0+0+0+0+0+0+0+0+0+0+0
|
||||
cpucycles observed persecond 2751500000...4258250000 with 4096 loops 5 microseconds
|
||||
cpucycles observed persecond 3289200000...4206875000 with 8192 loops 9 microseconds
|
||||
cpucycles observed persecond 3454789473...3900823530 with 16384 loops 18 microseconds
|
||||
cpucycles observed persecond 3452026315...3659888889 with 32768 loops 37 microseconds
|
||||
cpucycles observed persecond 3543770270...3650916667 with 65536 loops 73 microseconds
|
||||
cpucycles observed persecond 3567299319...3620662069 with 131072 loops 146 microseconds
|
||||
cpucycles observed persecond 3591373287...3618220690 with 262144 loops 291 microseconds
|
||||
cpucycles observed persecond 3597353344...3610774527 with 524288 loops 582 microseconds
|
||||
cpucycles observed persecond 3595899403...3603058071 with 1048576 loops 1172 microseconds
|
||||
</code></pre>
|
||||
<p>IBM z15:</p>
|
||||
<pre><code>cpucycles version 20230106
|
||||
cpucycles tracesetup 0 s390x-stckf precision 250 scaling 1.269531 only32 0
|
||||
cpucycles tracesetup 1 default-perfevent precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 2 default-mach precision 0 scaling 0.000000 only32 0
|
||||
cpucycles tracesetup 3 default-monotonic precision 272 scaling 5.200000 only32 0
|
||||
cpucycles tracesetup 4 default-gettimeofday precision 5400 scaling 5200.000000 only32 0
|
||||
cpucycles tracesetup 5 default-zero precision 0 scaling 0.000000 only32 0
|
||||
cpucycles persecond 5200000000
|
||||
cpucycles implementation s390x-stckf
|
||||
cpucycles median 48 +87+8+0-2+0+0+38-2+0+1-3+1+28+0+3-3+1+0+28+0-2+3+0-2+36+0+0+0+1+0+28+0-2+0+3-2+35+1+0-2+0+3+28+0-2+0+0-2+3+25+3+0-2+0+1+35+1+0+0-2+0+28+0
|
||||
cpucycles observed persecond 4948941176...5627733334 with 8192 loops 16 microseconds
|
||||
cpucycles observed persecond 4104125000...5515666667 with 16384 loops 7 microseconds
|
||||
cpucycles observed persecond 5047076923...5987818182 with 32768 loops 12 microseconds
|
||||
cpucycles observed persecond 5044846153...5475708334 with 65536 loops 25 microseconds
|
||||
cpucycles observed persecond 5141313725...5357428572 with 131072 loops 50 microseconds
|
||||
cpucycles observed persecond 5150892156...5257250000 with 262144 loops 101 microseconds
|
||||
cpucycles observed persecond 5183421568...5236549505 with 524288 loops 203 microseconds
|
||||
cpucycles observed persecond 5190282555...5216582717 with 1048576 loops 406 microseconds
|
||||
</code></pre><hr><font size=1><b>Version:</b>
|
||||
This is version 2023.01.06 of the "Counters" web page.
|
||||
</font>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user