Skip to content
This repository has been archived by the owner on Sep 13, 2022. It is now read-only.

Commit

Permalink
perf: use inner call instead of dispatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
KaoImin committed Jul 21, 2020
1 parent 2ccdf1a commit abbdfc2
Show file tree
Hide file tree
Showing 29 changed files with 695 additions and 654 deletions.
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,17 @@ muta-apm = "0.1.0-alpha.7"
futures-timer="3.0"

[dev-dependencies]
cita_trie = "2.0"
async-trait = "0.1"
toml = "0.5"
lazy_static = "1.4"
muta-codec-derive = "0.2"
asset = { path = "built-in-services/asset" }
multi-signature = { path = "built-in-services/multi-signature" }
authorization = { path = "built-in-services/authorization" }
metadata = { path = "built-in-services/metadata"}
util = { path = "built-in-services/util"}
rand = "0.7"
cita_trie = "2.0"
core-network = { path = "./core/network", features = ["diagnostic"] }
tokio = { version = "0.2", features = ["full"] }

Expand Down Expand Up @@ -83,3 +87,7 @@ crate-type = ["bin"]
name = "trust_metric"
path = "tests/trust_metric.rs"
required-features = [ "core-network/diagnostic" ]

[[bench]]
name = "bench_execute"
path = "benchmark/mod.rs"
24 changes: 24 additions & 0 deletions benchmark/bench.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use asset::types::TransferPayload;

use super::*;

#[rustfmt::skip]
/// Bench in Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz (8 x 2200)
/// 100 txs bench_execute ... bench: 11,299,912 ns/iter (+/- 3,402,276)
/// 1000 txs bench::bench_execute ... bench: 101,187,934 ns/iter (+/- 26,000,469)
#[bench]
fn bench_execute(b: &mut Bencher) {
let payload = TransferPayload {
asset_id: NATIVE_ASSET_ID.clone(),
to: FEE_INLET_ACCOUNT.clone(),
value: 1u64,
};

let req = TransactionRequest {
service_name: "asset".to_string(),
method: "transfer".to_string(),
payload: serde_json::to_string(&payload).unwrap(),
};

benchmark!(req, 1_000, b);
}
52 changes: 52 additions & 0 deletions benchmark/benchmark_genesis.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
timestamp = 0
prevhash = "0x44915be5b6c20b0678cf05fcddbbaa832e25d7e6ac538784cd5c24de00d47472"

[[services]]
name = "asset"
payload = '''
{
"id": "0xf56924db538e77bb5951eb5ff0d02b88983c49c45eea30e8ae3e7234b311436c",
"name": "HuobiToken",
"symbol": "HT",
"supply": 320000011,
"precision": 100000,
"issuer": "0xcff1002107105460941f797828f468667aa1a2db",
"fee_account": "0x503492f4bddc731a72b8caa806183f921c284f8e",
"fee": 10,
"admin": "0xcff1002107105460941f797828f468667aa1a2db",
"relayable": true
}
'''

# private key of this admin:
# 2b672bb959fa7a852d7259b129b65aee9c83b39f427d6f7bded1f58c4c9310c2
[[services]]
name = "governance"
payload = '''
{
"info": {
"admin": "0xcff1002107105460941f797828f468667aa1a2db",
"tx_failure_fee": 10,
"tx_floor_fee": 20,
"profit_deduct_rate_per_million": 3,
"tx_fee_discount": [
{
"threshold": 1000,
"discount_percent": 90
},
{
"threshold": 10000,
"discount_percent": 70
},
{
"threshold": 100000,
"discount_percent": 50
}
],
"miner_benefit": 10
},
"tx_fee_inlet_address": "0x503492f4bddc731a72b8caa806183f921c284f8e",
"miner_profit_outlet_address": "0xcff1002107105460941f797828f468667aa1a2db",
"miner_charge_map": []
}
'''
206 changes: 206 additions & 0 deletions benchmark/governance/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
mod types;

use std::cell::RefCell;
use std::convert::From;
use std::rc::Rc;

use bytes::Bytes;
use derive_more::{Display, From};

use binding_macro::{genesis, hook_after, service, tx_hook_after, tx_hook_before};
use protocol::traits::{ExecutorParams, ServiceResponse, ServiceSDK, StoreMap};
use protocol::types::{Address, Hash, ServiceContext, ServiceContextParams};

use asset::types::TransferPayload;
use asset::AssetService;
use types::{GovernanceInfo, InitGenesisPayload};

const INFO_KEY: &str = "admin";
const TX_FEE_INLET_KEY: &str = "fee_address";
const MINER_PROFIT_OUTLET_KEY: &str = "miner_address";
static ADMISSION_TOKEN: Bytes = Bytes::from_static(b"governance");

lazy_static::lazy_static! {
pub static ref NATIVE_ASSET_ID: Hash = Hash::from_hex("0xf56924db538e77bb5951eb5ff0d02b88983c49c45eea30e8ae3e7234b311436c").unwrap();
}

pub struct GovernanceService<SDK> {
sdk: SDK,
profits: Box<dyn StoreMap<Address, u64>>,
miners: Box<dyn StoreMap<Address, Address>>,

asset: AssetService<SDK>,
}

#[service]
impl<SDK: ServiceSDK> GovernanceService<SDK> {
pub fn new(mut sdk: SDK, asset: AssetService<SDK>) -> Self {
let profits: Box<dyn StoreMap<Address, u64>> = sdk.alloc_or_recover_map("profit");
let miners: Box<dyn StoreMap<Address, Address>> = sdk.alloc_or_recover_map("miner_address");
Self {
sdk,
profits,
miners,
asset,
}
}

#[genesis]
fn init_genesis(&mut self, payload: InitGenesisPayload) {
assert!(self.profits.is_empty());

let mut info = payload.info;
info.tx_fee_discount.sort();
self.sdk.set_value(INFO_KEY.to_string(), info);
self.sdk
.set_value(TX_FEE_INLET_KEY.to_string(), payload.tx_fee_inlet_address);
self.sdk.set_value(
MINER_PROFIT_OUTLET_KEY.to_string(),
payload.miner_profit_outlet_address,
);

for miner in payload.miner_charge_map.into_iter() {
self.miners
.insert(miner.address, miner.miner_charge_address);
}
}

#[tx_hook_before]
fn pledge_fee(&mut self, ctx: ServiceContext) -> ServiceResponse<String> {
let info = self
.sdk
.get_value::<_, GovernanceInfo>(&INFO_KEY.to_owned());
let tx_fee_inlet_address = self
.sdk
.get_value::<_, Address>(&TX_FEE_INLET_KEY.to_owned());

if info.is_none() || tx_fee_inlet_address.is_none() {
return ServiceError::MissingInfo.into();
}

let info = info.unwrap();
let tx_fee_inlet_address = tx_fee_inlet_address.unwrap();

// Pledge the tx failure fee before executed the transaction.
let ret = self.asset.transfer(ctx, TransferPayload {
asset_id: NATIVE_ASSET_ID.clone(),
to: tx_fee_inlet_address,
value: info.tx_failure_fee,
});

if ret.is_error() {
return ServiceResponse::from_error(ret.code, ret.error_message);
}

ServiceResponse::from_succeed("".to_owned())
}

#[tx_hook_after]
fn deduct_fee(&mut self, ctx: ServiceContext) -> ServiceResponse<String> {
let tx_fee_inlet_address = self
.sdk
.get_value::<_, Address>(&TX_FEE_INLET_KEY.to_owned());
if tx_fee_inlet_address.is_none() {
return ServiceError::MissingInfo.into();
}

let tx_fee_inlet_address = tx_fee_inlet_address.unwrap();

let ret = self.asset.transfer(ctx, TransferPayload {
asset_id: NATIVE_ASSET_ID.clone(),
to: tx_fee_inlet_address,
value: 1,
});

if ret.is_error() {
return ServiceResponse::from_error(ret.code, ret.error_message);
}

ServiceResponse::from_succeed("".to_owned())
}

#[hook_after]
fn handle_miner_profit(&mut self, params: &ExecutorParams) {
let info = self
.sdk
.get_value::<_, GovernanceInfo>(&INFO_KEY.to_owned());

let sender_address = self
.sdk
.get_value::<_, Address>(&MINER_PROFIT_OUTLET_KEY.to_owned());

if info.is_none() || sender_address.is_none() {
return;
}

let info = info.unwrap();
let sender_address = sender_address.unwrap();

let ctx_params = ServiceContextParams {
tx_hash: None,
nonce: None,
cycles_limit: params.cycles_limit,
cycles_price: 1,
cycles_used: Rc::new(RefCell::new(0)),
caller: sender_address,
height: params.height,
service_name: String::new(),
service_method: String::new(),
service_payload: String::new(),
extra: Some(ADMISSION_TOKEN.clone()),
timestamp: params.timestamp,
events: Rc::new(RefCell::new(vec![])),
};

let recipient_addr = if let Some(addr) = self.miners.get(&params.proposer) {
addr
} else {
params.proposer.clone()
};

let payload = TransferPayload {
asset_id: NATIVE_ASSET_ID.clone(),
to: recipient_addr,
value: info.miner_benefit,
};

let _ = self
.asset
.transfer(ServiceContext::new(ctx_params), payload);
}
}

#[derive(Debug, Display, From)]
pub enum ServiceError {
NonAuthorized,

#[display(fmt = "Can not get governance info")]
MissingInfo,

#[display(fmt = "calc overflow")]
Overflow,

#[display(fmt = "query balance failed")]
QueryBalance,

#[display(fmt = "Parsing payload to json failed {:?}", _0)]
JsonParse(serde_json::Error),
}

impl ServiceError {
fn code(&self) -> u64 {
match self {
ServiceError::NonAuthorized => 101,
ServiceError::JsonParse(_) => 102,
ServiceError::MissingInfo => 103,
ServiceError::Overflow => 104,
ServiceError::QueryBalance => 105,
}
}
}

impl<T: Default> From<ServiceError> for ServiceResponse<T> {
fn from(err: ServiceError) -> ServiceResponse<T> {
ServiceResponse::from_error(err.code(), err.to_string())
}
}
70 changes: 70 additions & 0 deletions benchmark/governance/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use std::cmp::Ordering;

use muta_codec_derive::RlpFixedCodec;
use serde::{Deserialize, Serialize};

use protocol::fixed_codec::{FixedCodec, FixedCodecError};
use protocol::types::{Address, Bytes};
use protocol::ProtocolResult;

#[derive(RlpFixedCodec, Deserialize, Serialize, Clone, Debug)]
pub struct InitGenesisPayload {
pub info: GovernanceInfo,
pub tx_fee_inlet_address: Address,
pub miner_profit_outlet_address: Address,
pub miner_charge_map: Vec<MinerChargeConfig>,
}

#[derive(RlpFixedCodec, Deserialize, Serialize, Clone, Debug)]
pub struct MinerChargeConfig {
pub address: Address,
pub miner_charge_address: Address,
}

#[derive(RlpFixedCodec, Deserialize, Serialize, Clone, Debug, Default)]
pub struct GovernanceInfo {
pub admin: Address,
pub tx_failure_fee: u64,
pub tx_floor_fee: u64,
pub profit_deduct_rate_per_million: u64,
pub tx_fee_discount: Vec<DiscountLevel>,
pub miner_benefit: u64,
}

#[derive(RlpFixedCodec, Deserialize, Serialize, Clone, Debug, Default, PartialEq, Eq)]
pub struct DiscountLevel {
pub threshold: u64,
pub discount_percent: u64,
}

impl PartialOrd for DiscountLevel {
fn partial_cmp(&self, other: &DiscountLevel) -> Option<Ordering> {
self.threshold.partial_cmp(&other.threshold)
}
}

impl Ord for DiscountLevel {
fn cmp(&self, other: &DiscountLevel) -> Ordering {
self.threshold.cmp(&other.threshold)
}
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct RecordProfitEvent {
pub owner: Address,
pub amount: u64,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct AccumulateProfitPayload {
pub address: Address,
pub accumulated_profit: u64,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct HookTransferFromPayload {
pub sender: Address,
pub recipient: Address,
pub value: u64,
pub memo: String,
}
Loading

0 comments on commit abbdfc2

Please sign in to comment.