Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Fee estimate RPC #1659

Merged
merged 1 commit into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ members = [
"util/types",
"util/future-executor",
"util/jsonrpc-types",
"util/fee-estimator",
"script/data-loader",
"db",
"resource",
Expand Down
2 changes: 1 addition & 1 deletion benches/benches/benchmarks/overall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use ckb_shared::{
Snapshot,
};
use ckb_store::ChainStore;
use ckb_tx_pool::{fee_rate::FeeRate, BlockAssemblerConfig, TxPoolConfig};
use ckb_tx_pool::{BlockAssemblerConfig, FeeRate, TxPoolConfig};
use ckb_types::{
bytes::Bytes,
core::{
Expand Down
2 changes: 2 additions & 0 deletions rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ckb-sync = { path = "../sync" }
ckb-chain = { path = "../chain" }
ckb-logger = { path = "../util/logger"}
ckb-network-alert = { path = "../util/network-alert" }
ckb-fee-estimator = { path = "../util/fee-estimator" }
jsonrpc-core = "10.1"
jsonrpc-derive = "10.1"
jsonrpc-http-server = { git = "https://github.com/nervosnetwork/jsonrpc", rev = "7c101f83a8fe34369c1b7a0e9b6721fcb0f91ee0" }
Expand Down Expand Up @@ -42,3 +43,4 @@ ckb-test-chain-utils = { path = "../util/test-chain-utils" }
tempfile = "3.0"
pretty_assertions = "0.6.1"
ckb-dao-utils = { path = "../util/dao/utils" }
rand = "0.7"
36 changes: 36 additions & 0 deletions rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ NOTE: This file is auto-generated. Please don't update this file directly; inste
* [`dry_run_transaction`](#dry_run_transaction)
* [`_compute_transaction_hash`](#_compute_transaction_hash)
* [`calculate_dao_maximum_withdraw`](#calculate_dao_maximum_withdraw)
* [`estimate_fee_rate`](#estimate_fee_rate)
* [`_compute_script_hash`](#_compute_script_hash)
* [`Indexer`](#indexer)
* [`index_lock_hash`](#index_lock_hash)
Expand Down Expand Up @@ -898,6 +899,41 @@ http://localhost:8114
}
```

### `estimate_fee_rate`

Estimate a fee rate (capacity/KB) for a transaction that to be committed in expect blocks.

This method estimate fee rate by sample transactions that collected from p2p network
expected_confirm_blocks must be between 3 and 1000
an error will return if samples is not enough


#### Examples

```bash
echo '{
"id": 2,
"jsonrpc": "2.0",
"method": "estimate_fee_rate",
"params": [
"0xa"
]
}' \
| tr -d '\n' \
| curl -H 'content-type: application/json' -d @- \
http://localhost:8114
```

```json
{
"id": 2,
"jsonrpc": "2.0",
"result": {
"fee_rate": "0x7d0"
}
}
```

### `_compute_script_hash`

Returns script hash of given transaction script
Expand Down
11 changes: 11 additions & 0 deletions rpc/json/rpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,17 @@
}
]
},
{
"description": "Estimate a fee rate (capacity/KB) for a transaction that to be committed in expect blocks.\n\nThis method estimate fee rate by sample transactions that collected from p2p network\nexpected_confirm_blocks must be between 3 and 1000\nan error will return if samples is not enough",
"method": "estimate_fee_rate",
"module": "experiment",
"params": [
"0xa"
],
"result": {
"fee_rate": "0x7d0"
}
},
{
"description": "Send new transaction into transaction pool\n\nIf <block_hash> of <previsous_output> is not specified, loads the corresponding input cell. If <block_hash> is specified, load the corresponding input cell only if the corresponding block exist and contain this cell as output.",
"method": "send_transaction",
Expand Down
43 changes: 42 additions & 1 deletion rpc/src/module/experiment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::error::RPCError;
use ckb_dao::DaoCalculator;
use ckb_jsonrpc_types::{Capacity, DryRunResult, OutPoint, Script, Transaction};
use ckb_fee_estimator::MAX_CONFIRM_BLOCKS;
use ckb_jsonrpc_types::{
Capacity, DryRunResult, EstimateResult, OutPoint, Script, Transaction, Uint64,
};
use ckb_logger::error;
use ckb_shared::{shared::Shared, Snapshot};
use ckb_store::ChainStore;
Expand Down Expand Up @@ -31,6 +34,10 @@ pub trait ExperimentRpc {
#[rpc(name = "calculate_dao_maximum_withdraw")]
fn calculate_dao_maximum_withdraw(&self, _out_point: OutPoint, _hash: H256)
-> Result<Capacity>;

// Estimate fee
#[rpc(name = "estimate_fee_rate")]
fn estimate_fee_rate(&self, expect_confirm_blocks: Uint64) -> Result<EstimateResult>;
}

pub(crate) struct ExperimentRpcImpl {
Expand Down Expand Up @@ -65,6 +72,40 @@ impl ExperimentRpc for ExperimentRpcImpl {
}
}
}

fn estimate_fee_rate(&self, expect_confirm_blocks: Uint64) -> Result<EstimateResult> {
let expect_confirm_blocks = expect_confirm_blocks.value() as usize;
// A tx need 1 block to propose, then 2 block to get confirmed
// so at least confirm blocks is 3 blocks.
if expect_confirm_blocks < 3 || expect_confirm_blocks > MAX_CONFIRM_BLOCKS {
return Err(RPCError::custom(
RPCError::Invalid,
format!(
"expect_confirm_blocks should between 3 and {}, got {}",
MAX_CONFIRM_BLOCKS, expect_confirm_blocks
),
));
}

let tx_pool = self.shared.tx_pool_controller();
let fee_rate = tx_pool.estimate_fee_rate(expect_confirm_blocks);
if let Err(e) = fee_rate {
error!("send estimate_fee_rate request error {}", e);
return Err(Error::internal_error());
};
let fee_rate = fee_rate.unwrap();

if fee_rate.as_u64() == 0 {
return Err(RPCError::custom(
RPCError::Invalid,
"collected samples is not enough, please make sure node has peers and try later"
.into(),
));
}
Ok(EstimateResult {
fee_rate: fee_rate.as_u64().into(),
})
}
}

// DryRunner dry run given transaction, and return the result, including execution cycles.
Expand Down
2 changes: 1 addition & 1 deletion rpc/src/module/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ckb_logger::error;
use ckb_network::PeerIndex;
use ckb_shared::shared::Shared;
use ckb_sync::SyncSharedState;
use ckb_tx_pool::{error::SubmitTxError, fee_rate::FeeRate};
use ckb_tx_pool::{error::SubmitTxError, FeeRate};
use ckb_types::{
core::{self},
packed,
Expand Down
2 changes: 1 addition & 1 deletion rpc/src/service_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use ckb_network_alert::{notifier::Notifier as AlertNotifier, verifier::Verifier
use ckb_shared::shared::Shared;
use ckb_sync::SyncSharedState;
use ckb_sync::Synchronizer;
use ckb_tx_pool::fee_rate::FeeRate;
use ckb_tx_pool::FeeRate;
use ckb_util::Mutex;
use jsonrpc_core::IoHandler;
use std::sync::Arc;
Expand Down
33 changes: 31 additions & 2 deletions rpc/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ use ckb_shared::{
use ckb_store::ChainStore;
use ckb_sync::{SyncSharedState, Synchronizer};
use ckb_test_chain_utils::{always_success_cell, always_success_cellbase};
use ckb_tx_pool::fee_rate::FeeRate;
use ckb_tx_pool::FeeRate;
use ckb_types::{
core::{
capacity_bytes, cell::resolve_transaction, BlockBuilder, BlockView, Capacity,
EpochNumberWithFraction, HeaderView, TransactionBuilder, TransactionView,
},
h256,
packed::{AlertBuilder, CellDep, CellInput, CellOutputBuilder, OutPoint, RawAlertBuilder},
packed::{
AlertBuilder, Byte32, CellDep, CellInput, CellOutputBuilder, OutPoint, RawAlertBuilder,
},
prelude::*,
H256,
};
Expand All @@ -36,6 +38,7 @@ use jsonrpc_http_server::ServerBuilder;
use jsonrpc_server_utils::cors::AccessControlAllowOrigin;
use jsonrpc_server_utils::hosts::DomainsValidation;
use pretty_assertions::assert_eq as pretty_assert_eq;
use rand::{thread_rng, Rng};
use reqwest;
use serde::ser::Serialize;
use serde_derive::{Deserialize, Serialize};
Expand Down Expand Up @@ -146,17 +149,42 @@ fn setup_node(height: u64) -> (Shared, ChainController, RpcServer) {
.build()
.unwrap();
let chain_controller = ChainService::new(shared.clone(), table).start::<&str>(None);
let tx_pool_controller = shared.tx_pool_controller();

// Build chain, insert [1, height) blocks
let mut parent = always_success_consensus().genesis_block;

// prepare fee estimator samples
let sample_txs: Vec<Byte32> = (0..30)
.map(|_| {
let mut buf = [0u8; 32];
let mut rng = thread_rng();
rng.fill(&mut buf);
buf.pack()
})
.collect();
let fee_rate = FeeRate::from_u64(2_000);
let send_height = height.saturating_sub(9);

for _ in 0..height {
let block = next_block(&shared, &parent.header());
chain_controller
.process_block(Arc::new(block.clone()))
.expect("processing new block should be ok");
// Fake fee estimator samples
if block.header().number() == send_height {
for tx_hash in sample_txs.clone() {
tx_pool_controller
.estimator_track_tx(tx_hash, fee_rate, send_height)
.expect("prepare estimator samples");
}
}
parent = block;
}
// mark txs as confirmed
tx_pool_controller
.estimator_process_block(height + 1, sample_txs.into_iter())
.expect("process estimator samples");

// Start network services
let dir = tempfile::tempdir()
Expand Down Expand Up @@ -402,6 +430,7 @@ fn params_of(shared: &Shared, method: &str) -> Value {
let json_script: ckb_jsonrpc_types::Script = script.into();
vec![json!(json_script)]
}
"estimate_fee_rate" => vec![json!("0xa")],
"calculate_dao_maximum_withdraw" => vec![json!(always_success_out_point), json!(tip_hash)],
"get_block_template" => vec![json!(null), json!(null), json!(null)],
"submit_block" => {
Expand Down
2 changes: 1 addition & 1 deletion sync/src/relayer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::BAD_MESSAGE_BAN_TIME;
use ckb_chain::chain::ChainController;
use ckb_logger::{debug_target, info_target, trace_target};
use ckb_network::{CKBProtocolContext, CKBProtocolHandler, PeerIndex, TargetSession};
use ckb_tx_pool::fee_rate::FeeRate;
use ckb_tx_pool::FeeRate;
use ckb_types::{
core::{self},
packed::{self, Byte32, ProposalShortId},
Expand Down
2 changes: 1 addition & 1 deletion sync/src/relayer/tests/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ckb_network::{
use ckb_shared::shared::{Shared, SharedBuilder};
use ckb_store::ChainStore;
use ckb_test_chain_utils::always_success_cell;
use ckb_tx_pool::fee_rate::FeeRate;
use ckb_tx_pool::FeeRate;
use ckb_types::prelude::*;
use ckb_types::{
bytes::Bytes,
Expand Down
1 change: 1 addition & 0 deletions test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ fn all_specs() -> SpecMap {
Box::new(ReferenceHeaderMaturity),
Box::new(ValidSince),
Box::new(SendLowFeeRateTx),
Box::new(FeeEstimate),
Box::new(DifferentTxsWithSameInput),
Box::new(CompactBlockEmpty),
Box::new(CompactBlockEmptyParentUnknown),
Expand Down
14 changes: 12 additions & 2 deletions test/src/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use ckb_jsonrpc_types::{
Alert, BannedAddr, Block, BlockNumber, BlockReward, BlockTemplate, BlockView, Capacity,
CellOutputWithOutPoint, CellTransaction, CellWithStatus, ChainInfo, Cycle, DryRunResult,
EpochNumber, EpochView, HeaderView, LiveCell, LockHashIndexState, Node, OutPoint, PeerState,
Timestamp, Transaction, TransactionWithStatus, TxPoolInfo, Uint64, Version,
EpochNumber, EpochView, EstimateResult, HeaderView, LiveCell, LockHashIndexState, Node,
OutPoint, PeerState, Timestamp, Transaction, TransactionWithStatus, TxPoolInfo, Uint64,
Version,
};
use ckb_types::core::{
BlockNumber as CoreBlockNumber, Capacity as CoreCapacity, EpochNumber as CoreEpochNumber,
Expand Down Expand Up @@ -359,6 +360,14 @@ impl RpcClient {
.expect("rpc call get_cellbase_output_capacity_details")
.expect("get_cellbase_output_capacity_details return none")
}

pub fn estimate_fee_rate(&self, expect_confirm_blocks: Uint64) -> EstimateResult {
self.inner()
.lock()
.estimate_fee_rate(expect_confirm_blocks)
.call()
.expect("rpc call estimate_fee_rate")
}
}

jsonrpc_client!(pub struct Inner {
Expand Down Expand Up @@ -420,4 +429,5 @@ jsonrpc_client!(pub struct Inner {
pub fn calculate_dao_maximum_withdraw(&mut self, _out_point: OutPoint, _hash: H256) -> RpcRequest<Capacity>;
pub fn get_cellbase_output_capacity_details(&mut self, _hash: H256) -> RpcRequest<Option<BlockReward>>;
pub fn broadcast_transaction(&mut self, tx: Transaction, cycles: Cycle) -> RpcRequest<H256>;
pub fn estimate_fee_rate(&mut self, expect_confirm_blocks: Uint64) -> RpcRequest<EstimateResult>;
});
2 changes: 1 addition & 1 deletion test/src/specs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use ckb_app_config::CKBAppConfig;
use ckb_chain_spec::ChainSpec;
use ckb_network::{ProtocolId, ProtocolVersion};
use ckb_sync::NetworkProtocol;
use ckb_tx_pool::fee_rate::FeeRate;
use ckb_tx_pool::FeeRate;

#[macro_export]
macro_rules! name {
Expand Down
2 changes: 1 addition & 1 deletion test/src/specs/relay/transaction_relay_low_fee_rate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::utils::wait_until;
use crate::{Net, Spec, DEFAULT_TX_PROPOSAL_WINDOW};
use ckb_app_config::CKBAppConfig;
use ckb_jsonrpc_types::Status;
use ckb_tx_pool::fee_rate::FeeRate;
use ckb_tx_pool::FeeRate;
use ckb_types::{core::TransactionView, packed, prelude::*};
use log::info;

Expand Down
Loading