diff --git a/rpc/README.md b/rpc/README.md
index 6625be5773..ecd547a817 100644
--- a/rpc/README.md
+++ b/rpc/README.md
@@ -104,6 +104,7 @@ The crate `ckb-rpc`'s minimum supported rustc version is 1.71.1.
* [Module Pool](#module-pool) [👉 OpenRPC spec](http://playground.open-rpc.org/?uiSchema[appBar][ui:title]=CKB-Pool&uiSchema[appBar][ui:splitView]=false&uiSchema[appBar][ui:examplesDropdown]=false&uiSchema[appBar][ui:logoUrl]=https://raw.githubusercontent.com/nervosnetwork/ckb-rpc-resources/develop/ckb-logo.jpg&schemaUrl=https://raw.githubusercontent.com/nervosnetwork/ckb-rpc-resources/develop/json/pool_rpc_doc.json)
* [Method `send_transaction`](#pool-send_transaction)
+ * [Method `test_tx_pool_accept`](#pool-test_tx_pool_accept)
* [Method `remove_transaction`](#pool-remove_transaction)
* [Method `tx_pool_info`](#pool-tx_pool_info)
* [Method `clear_tx_pool`](#pool-clear_tx_pool)
@@ -162,6 +163,7 @@ The crate `ckb-rpc`'s minimum supported rustc version is 1.71.1.
* [Type `DeploymentInfo`](#type-deploymentinfo)
* [Type `DeploymentState`](#type-deploymentstate)
* [Type `DeploymentsInfo`](#type-deploymentsinfo)
+ * [Type `EntryCompleted`](#type-entrycompleted)
* [Type `EpochNumber`](#type-epochnumber)
* [Type `EpochNumber`](#type-epochnumber)
* [Type `EpochNumberWithFraction`](#type-epochnumberwithfraction)
@@ -4420,6 +4422,114 @@ Response
}
```
+
+#### Method `test_tx_pool_accept`
+* `test_tx_pool_accept(tx, outputs_validator)`
+ * `tx`: [`Transaction`](#type-transaction)
+ * `outputs_validator`: [`OutputsValidator`](#type-outputsvalidator) `|` `null`
+* result: [`EntryCompleted`](#type-entrycompleted)
+
+Test if a transaction can be accepted by the transaction pool without inserting it into the pool or rebroadcasting it to peers.
+The parameters and errors of this method are the same as `send_transaction`.
+
+###### Params
+
+* `transaction` - The transaction.
+* `outputs_validator` - Validates the transaction outputs before entering the tx-pool. (**Optional**, default is "passthrough").
+
+###### Errors
+
+* [`PoolRejectedTransactionByOutputsValidator (-1102)`](../enum.RPCError.html#variant.PoolRejectedTransactionByOutputsValidator) - The transaction is rejected by the validator specified by `outputs_validator`. If you really want to send transactions with advanced scripts, please set `outputs_validator` to "passthrough".
+* [`PoolRejectedTransactionByMinFeeRate (-1104)`](../enum.RPCError.html#variant.PoolRejectedTransactionByMinFeeRate) - The transaction fee rate must be greater than or equal to the config option `tx_pool.min_fee_rate`.
+* [`PoolRejectedTransactionByMaxAncestorsCountLimit (-1105)`](../enum.RPCError.html#variant.PoolRejectedTransactionByMaxAncestorsCountLimit) - The ancestors count must be greater than or equal to the config option `tx_pool.max_ancestors_count`.
+* [`PoolIsFull (-1106)`](../enum.RPCError.html#variant.PoolIsFull) - Pool is full.
+* [`PoolRejectedDuplicatedTransaction (-1107)`](../enum.RPCError.html#variant.PoolRejectedDuplicatedTransaction) - The transaction is already in the pool.
+* [`TransactionFailedToResolve (-301)`](../enum.RPCError.html#variant.TransactionFailedToResolve) - Failed to resolve the referenced cells and headers used in the transaction, as inputs or dependencies.
+* [`TransactionFailedToVerify (-302)`](../enum.RPCError.html#variant.TransactionFailedToVerify) - Failed to verify the transaction.
+
+###### Examples
+
+Request
+
+```json
+{
+ "id": 42,
+ "jsonrpc": "2.0",
+ "method": "test_tx_pool_accept",
+ "params": [
+ {
+ "cell_deps": [
+ {
+ "dep_type": "code",
+ "out_point": {
+ "index": "0x0",
+ "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
+ }
+ }
+ ],
+ "header_deps": [
+ "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed"
+ ],
+ "inputs": [
+ {
+ "previous_output": {
+ "index": "0x0",
+ "tx_hash": "0x075fe030c1f4725713c5aacf41c2f59b29b284008fdb786e5efd8a058be51d0c"
+ },
+ "since": "0x0"
+ }
+ ],
+ "outputs": [
+ {
+ "capacity": "0x2431ac129",
+ "lock": {
+ "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
+ "hash_type": "data",
+ "args": "0x"
+ },
+ "type": null
+ }
+ ],
+ "outputs_data": [
+ "0x"
+ ],
+ "version": "0x0",
+ "witnesses": []
+ },
+ "passthrough"
+ ]
+}
+```
+
+Response
+
+```json
+{
+ "id": 42,
+ "jsonrpc": "2.0",
+ "result": {
+ "cycles": "0x219",
+ "fee": "0x2a66f36e90"
+ }
+}
+```
+
+
+The response looks like below if the transaction pool check fails
+
+```text
+{
+ "id": 42,
+ "jsonrpc": "2.0",
+ "result": null,
+ "error": {
+ "code": -1107,
+ "data": "Duplicated(Byte32(0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3))",
+ "message": "PoolRejectedDuplicatedTransaction: Transaction(Byte32(0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3)) already exists in transaction_pool"
+ }
+}
+```
+
#### Method `remove_transaction`
* `remove_transaction(tx_hash)`
@@ -5739,6 +5849,17 @@ Chain information.
* `hash`: [`H256`](#type-h256) - requested block hash
+### Type `EntryCompleted`
+Transaction's verify result by test_tx_pool_accept
+
+#### Fields
+
+`EntryCompleted` is a JSON object with the following fields.
+
+* `cycles`: [`Uint64`](#type-uint64) - Cached tx cycles
+
+* `fee`: [`Uint64`](#type-uint64) - Cached tx fee
+
### Type `EpochNumber`
Consecutive epoch number starting from 0.
diff --git a/rpc/src/module/pool.rs b/rpc/src/module/pool.rs
index dc081ae5fc..aab1071e0d 100644
--- a/rpc/src/module/pool.rs
+++ b/rpc/src/module/pool.rs
@@ -3,10 +3,11 @@ use async_trait::async_trait;
use ckb_chain_spec::consensus::Consensus;
use ckb_constant::hardfork::{mainnet, testnet};
use ckb_jsonrpc_types::{
- OutputsValidator, PoolTxDetailInfo, RawTxPool, Script, Transaction, TxPoolInfo,
+ EntryCompleted, OutputsValidator, PoolTxDetailInfo, RawTxPool, Script, Transaction, TxPoolInfo,
};
use ckb_logger::error;
use ckb_shared::shared::Shared;
+use ckb_types::core::TransactionView;
use ckb_types::{core, packed, prelude::*, H256};
use ckb_verification::{Since, SinceMetric};
use jsonrpc_core::Result;
@@ -109,6 +110,113 @@ pub trait PoolRpc {
outputs_validator: Option,
) -> Result;
+ /// Test if a transaction can be accepted by the transaction pool without inserting it into the pool or rebroadcasting it to peers.
+ /// The parameters and errors of this method are the same as `send_transaction`.
+ ///
+ /// ## Params
+ ///
+ /// * `transaction` - The transaction.
+ /// * `outputs_validator` - Validates the transaction outputs before entering the tx-pool. (**Optional**, default is "passthrough").
+ ///
+ /// ## Errors
+ ///
+ /// * [`PoolRejectedTransactionByOutputsValidator (-1102)`](../enum.RPCError.html#variant.PoolRejectedTransactionByOutputsValidator) - The transaction is rejected by the validator specified by `outputs_validator`. If you really want to send transactions with advanced scripts, please set `outputs_validator` to "passthrough".
+ /// * [`PoolRejectedTransactionByMinFeeRate (-1104)`](../enum.RPCError.html#variant.PoolRejectedTransactionByMinFeeRate) - The transaction fee rate must be greater than or equal to the config option `tx_pool.min_fee_rate`.
+ /// * [`PoolRejectedTransactionByMaxAncestorsCountLimit (-1105)`](../enum.RPCError.html#variant.PoolRejectedTransactionByMaxAncestorsCountLimit) - The ancestors count must be greater than or equal to the config option `tx_pool.max_ancestors_count`.
+ /// * [`PoolIsFull (-1106)`](../enum.RPCError.html#variant.PoolIsFull) - Pool is full.
+ /// * [`PoolRejectedDuplicatedTransaction (-1107)`](../enum.RPCError.html#variant.PoolRejectedDuplicatedTransaction) - The transaction is already in the pool.
+ /// * [`TransactionFailedToResolve (-301)`](../enum.RPCError.html#variant.TransactionFailedToResolve) - Failed to resolve the referenced cells and headers used in the transaction, as inputs or dependencies.
+ /// * [`TransactionFailedToVerify (-302)`](../enum.RPCError.html#variant.TransactionFailedToVerify) - Failed to verify the transaction.
+ ///
+ /// ## Examples
+ ///
+ /// Request
+ ///
+ /// ```json
+ /// {
+ /// "id": 42,
+ /// "jsonrpc": "2.0",
+ /// "method": "test_tx_pool_accept",
+ /// "params": [
+ /// {
+ /// "cell_deps": [
+ /// {
+ /// "dep_type": "code",
+ /// "out_point": {
+ /// "index": "0x0",
+ /// "tx_hash": "0xa4037a893eb48e18ed4ef61034ce26eba9c585f15c9cee102ae58505565eccc3"
+ /// }
+ /// }
+ /// ],
+ /// "header_deps": [
+ /// "0x7978ec7ce5b507cfb52e149e36b1a23f6062ed150503c85bbf825da3599095ed"
+ /// ],
+ /// "inputs": [
+ /// {
+ /// "previous_output": {
+ /// "index": "0x0",
+ /// "tx_hash": "0x075fe030c1f4725713c5aacf41c2f59b29b284008fdb786e5efd8a058be51d0c"
+ /// },
+ /// "since": "0x0"
+ /// }
+ /// ],
+ /// "outputs": [
+ /// {
+ /// "capacity": "0x2431ac129",
+ /// "lock": {
+ /// "code_hash": "0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5",
+ /// "hash_type": "data",
+ /// "args": "0x"
+ /// },
+ /// "type": null
+ /// }
+ /// ],
+ /// "outputs_data": [
+ /// "0x"
+ /// ],
+ /// "version": "0x0",
+ /// "witnesses": []
+ /// },
+ /// "passthrough"
+ /// ]
+ /// }
+ /// ```
+ ///
+ /// Response
+ ///
+ /// ```json
+ /// {
+ /// "id": 42,
+ /// "jsonrpc": "2.0",
+ /// "result": {
+ /// "cycles": "0x219",
+ /// "fee": "0x2a66f36e90"
+ /// }
+ /// }
+ /// ```
+ ///
+ ///
+ /// The response looks like below if the transaction pool check fails
+ ///
+ /// ```text
+ /// {
+ /// "id": 42,
+ /// "jsonrpc": "2.0",
+ /// "result": null,
+ /// "error": {
+ /// "code": -1107,
+ /// "data": "Duplicated(Byte32(0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3))",
+ /// "message": "PoolRejectedDuplicatedTransaction: Transaction(Byte32(0xa0ef4eb5f4ceeb08a4c8524d84c5da95dce2f608e0ca2ec8091191b0f330c6e3)) already exists in transaction_pool"
+ /// }
+ /// }
+ /// ```
+ #[rpc(name = "test_tx_pool_accept")]
+ fn test_tx_pool_accept(
+ &self,
+ tx: Transaction,
+ outputs_validator: Option,
+ ) -> Result;
+
/// Removes a transaction and all transactions which depends on it from tx pool if it exists.
///
/// ## Params
@@ -359,6 +467,33 @@ impl PoolRpcImpl {
well_known_type_scripts,
}
}
+
+ fn check_output_validator(
+ &self,
+ outputs_validator: Option,
+ tx: &TransactionView,
+ ) -> Result<()> {
+ if let Err(e) = match outputs_validator {
+ None | Some(OutputsValidator::Passthrough) => Ok(()),
+ Some(OutputsValidator::WellKnownScriptsOnly) => WellKnownScriptsOnlyValidator::new(
+ self.shared.consensus(),
+ &self.well_known_lock_scripts,
+ &self.well_known_type_scripts,
+ )
+ .validate(tx),
+ } {
+ return Err(RPCError::custom_with_data(
+ RPCError::PoolRejectedTransactionByOutputsValidator,
+ format!(
+ "The transaction is rejected by OutputsValidator set in params[1]: {}. \
+ Please check the related information in https://github.com/nervosnetwork/ckb/wiki/Transaction-%C2%BB-Default-Outputs-Validator",
+ outputs_validator.unwrap_or(OutputsValidator::WellKnownScriptsOnly).json_display()
+ ),
+ e,
+ ));
+ }
+ Ok(())
+ }
}
/// Build well known lock scripts
@@ -452,25 +587,7 @@ impl PoolRpc for PoolRpcImpl {
let tx: packed::Transaction = tx.into();
let tx: core::TransactionView = tx.into_view();
- if let Err(e) = match outputs_validator {
- None | Some(OutputsValidator::Passthrough) => Ok(()),
- Some(OutputsValidator::WellKnownScriptsOnly) => WellKnownScriptsOnlyValidator::new(
- self.shared.consensus(),
- &self.well_known_lock_scripts,
- &self.well_known_type_scripts,
- )
- .validate(&tx),
- } {
- return Err(RPCError::custom_with_data(
- RPCError::PoolRejectedTransactionByOutputsValidator,
- format!(
- "The transaction is rejected by OutputsValidator set in params[1]: {}. \
- Please check the related information in https://github.com/nervosnetwork/ckb/wiki/Transaction-%C2%BB-Default-Outputs-Validator",
- outputs_validator.unwrap_or(OutputsValidator::WellKnownScriptsOnly).json_display()
- ),
- e,
- ));
- }
+ self.check_output_validator(outputs_validator, &tx)?;
let tx_pool = self.shared.tx_pool_controller();
let submit_tx = tx_pool.submit_local_tx(tx.clone());
@@ -487,6 +604,31 @@ impl PoolRpc for PoolRpcImpl {
}
}
+ fn test_tx_pool_accept(
+ &self,
+ tx: Transaction,
+ outputs_validator: Option,
+ ) -> Result {
+ let tx: packed::Transaction = tx.into();
+ let tx: core::TransactionView = tx.into_view();
+
+ self.check_output_validator(outputs_validator, &tx)?;
+
+ let tx_pool = self.shared.tx_pool_controller();
+
+ let test_accept_tx_reslt = tx_pool.test_accept_tx(tx).map_err(|e| {
+ error!("Send test_tx_pool_accept_tx request error {}", e);
+ RPCError::ckb_internal_error(e)
+ })?;
+
+ test_accept_tx_reslt
+ .map(|test_accept_result| test_accept_result.into())
+ .map_err(|reject| {
+ error!("Send test_tx_pool_accept_tx request error {}", reject);
+ RPCError::from_submit_transaction_reject(&reject)
+ })
+ }
+
fn remove_transaction(&self, tx_hash: H256) -> Result {
let tx_pool = self.shared.tx_pool_controller();
diff --git a/tx-pool/src/process.rs b/tx-pool/src/process.rs
index ee432eda5e..39627ccf9a 100644
--- a/tx-pool/src/process.rs
+++ b/tx-pool/src/process.rs
@@ -339,6 +339,21 @@ impl TxPoolService {
}
}
+ pub(crate) async fn test_accept_tx(&self, tx: TransactionView) -> Result {
+ // non contextual verify first
+ self.non_contextual_verify(&tx, None)?;
+
+ if self.chunk_contains(&tx).await {
+ return Err(Reject::Duplicated(tx.hash()));
+ }
+
+ if self.orphan_contains(&tx).await {
+ debug!("reject tx {} already in orphan pool", tx.hash());
+ return Err(Reject::Duplicated(tx.hash()));
+ }
+ self._test_accept_tx(tx.clone()).await
+ }
+
pub(crate) async fn process_tx(
&self,
tx: TransactionView,
@@ -857,6 +872,29 @@ impl TxPoolService {
Some((Ok(verified), submit_snapshot))
}
+ pub(crate) async fn _test_accept_tx(&self, tx: TransactionView) -> Result {
+ let tx_hash = tx.hash();
+
+ let (pre_check_ret, snapshot) = self.pre_check(&tx).await;
+
+ let (_tip_hash, rtx, status, _fee, _tx_size) = pre_check_ret?;
+
+ // skip check the delay window
+
+ let verify_cache = self.fetch_tx_verify_cache(&tx_hash).await;
+ let max_cycles = self.consensus.max_block_cycles();
+ let tip_header = snapshot.tip_header();
+ let tx_env = Arc::new(status.with_env(tip_header));
+
+ verify_rtx(
+ Arc::clone(&snapshot),
+ Arc::clone(&rtx),
+ tx_env,
+ &verify_cache,
+ max_cycles,
+ )
+ }
+
pub(crate) async fn update_tx_pool_for_reorg(
&self,
detached_blocks: VecDeque,
diff --git a/tx-pool/src/service.rs b/tx-pool/src/service.rs
index 8ad6300a5b..64f17f4aa9 100644
--- a/tx-pool/src/service.rs
+++ b/tx-pool/src/service.rs
@@ -18,7 +18,7 @@ use ckb_logger::{error, info};
use ckb_network::{NetworkController, PeerIndex};
use ckb_snapshot::Snapshot;
use ckb_stop_handler::new_tokio_exit_rx;
-use ckb_types::core::tx_pool::{PoolTxDetailInfo, TransactionWithStatus, TxStatus};
+use ckb_types::core::tx_pool::{EntryCompleted, PoolTxDetailInfo, TransactionWithStatus, TxStatus};
use ckb_types::{
core::{
tx_pool::{Reject, TxPoolEntryInfo, TxPoolIds, TxPoolInfo, TRANSACTION_SIZE_LIMIT},
@@ -74,6 +74,8 @@ type BlockTemplateArgs = (Option, Option, Option);
pub(crate) type SubmitTxResult = Result<(), Reject>;
+pub(crate) type TestAcceptTxResult = Result;
+
type GetTxStatusResult = Result<(TxStatus, Option), AnyError>;
type GetTransactionWithStatusResult = Result;
type FetchTxsWithCyclesResult = Vec<(ProposalShortId, (TransactionView, Cycle))>;
@@ -89,6 +91,7 @@ pub(crate) enum Message {
BlockTemplate(Request),
SubmitLocalTx(Request),
RemoveLocalTx(Request),
+ TestAcceptTx(Request),
SubmitRemoteTx(Request<(TransactionView, Cycle, PeerIndex), ()>),
NotifyTxs(Notify>),
FreshProposalsFilter(Request, Vec>),
@@ -225,6 +228,13 @@ impl TxPoolController {
send_message!(self, SubmitLocalTx, tx)
}
+ /// test if a tx can be accepted by tx-pool
+ /// Won't be broadcasted to network
+ /// won't be insert to tx-pool
+ pub fn test_accept_tx(&self, tx: TransactionView) -> Result {
+ send_message!(self, TestAcceptTx, tx)
+ }
+
/// Remove tx from tx-pool
pub fn remove_local_tx(&self, tx_hash: Byte32) -> Result {
send_message!(self, RemoveLocalTx, tx_hash)
@@ -696,6 +706,15 @@ async fn process(mut service: TxPoolService, message: Message) {
error!("Responder sending remove_tx result failed {:?}", e);
};
}
+ Message::TestAcceptTx(Request {
+ responder,
+ arguments: tx,
+ }) => {
+ let result = service.test_accept_tx(tx).await;
+ if let Err(e) = responder.send(result.map(|r| r.into())) {
+ error!("Responder sending test_accept_tx result failed {:?}", e);
+ };
+ }
Message::SubmitRemoteTx(Request {
responder,
arguments: (tx, declared_cycles, peer),
diff --git a/util/jsonrpc-types/src/lib.rs b/util/jsonrpc-types/src/lib.rs
index a183318d33..ac70de3ec3 100644
--- a/util/jsonrpc-types/src/lib.rs
+++ b/util/jsonrpc-types/src/lib.rs
@@ -45,8 +45,9 @@ pub use self::net::{
RemoteNodeProtocol, SyncState,
};
pub use self::pool::{
- AncestorsScoreSortKey, OutputsValidator, PoolTransactionEntry, PoolTransactionReject,
- PoolTxDetailInfo, RawTxPool, TxPoolEntries, TxPoolEntry, TxPoolIds, TxPoolInfo,
+ AncestorsScoreSortKey, EntryCompleted, OutputsValidator, PoolTransactionEntry,
+ PoolTransactionReject, PoolTxDetailInfo, RawTxPool, TxPoolEntries, TxPoolEntry, TxPoolIds,
+ TxPoolInfo,
};
pub use self::proposal_short_id::ProposalShortId;
pub use self::subscription::Topic;
diff --git a/util/jsonrpc-types/src/pool.rs b/util/jsonrpc-types/src/pool.rs
index 5fb26901f4..18f2052dd1 100644
--- a/util/jsonrpc-types/src/pool.rs
+++ b/util/jsonrpc-types/src/pool.rs
@@ -350,3 +350,21 @@ impl From for PoolTransactionReject {
}
}
}
+
+/// Transaction's verify result by test_tx_pool_accept
+#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug, JsonSchema)]
+pub struct EntryCompleted {
+ /// Cached tx cycles
+ pub cycles: Cycle,
+ /// Cached tx fee
+ pub fee: Capacity,
+}
+
+impl From for EntryCompleted {
+ fn from(value: ckb_types::core::EntryCompleted) -> Self {
+ Self {
+ cycles: value.cycles.into(),
+ fee: value.fee.into(),
+ }
+ }
+}
diff --git a/util/types/src/core/mod.rs b/util/types/src/core/mod.rs
index ae8602a577..5b0cbdb5b3 100644
--- a/util/types/src/core/mod.rs
+++ b/util/types/src/core/mod.rs
@@ -34,7 +34,7 @@ pub use extras::{BlockExt, EpochExt, EpochNumberWithFraction, TransactionInfo};
pub use fee_rate::FeeRate;
pub use reward::{BlockEconomicState, BlockIssuance, BlockReward, MinerReward};
pub use transaction_meta::{TransactionMeta, TransactionMetaBuilder};
-pub use tx_pool::TransactionWithStatus;
+pub use tx_pool::{EntryCompleted, TransactionWithStatus};
pub use views::{
BlockView, ExtraHashView, HeaderView, TransactionView, UncleBlockVecView, UncleBlockView,
};
diff --git a/util/types/src/core/tx_pool.rs b/util/types/src/core/tx_pool.rs
index 0a6f3187e8..1ca23a061f 100644
--- a/util/types/src/core/tx_pool.rs
+++ b/util/types/src/core/tx_pool.rs
@@ -395,3 +395,12 @@ impl PoolTxDetailInfo {
}
}
}
+
+/// A Tx CacheEntry
+#[derive(Clone, PartialEq, Eq, Debug, Default)]
+pub struct EntryCompleted {
+ /// Cached tx cycles
+ pub cycles: Cycle,
+ /// Cached tx fee
+ pub fee: Capacity,
+}
diff --git a/verification/src/cache.rs b/verification/src/cache.rs
index b7ceab66ab..4165f4d1db 100644
--- a/verification/src/cache.rs
+++ b/verification/src/cache.rs
@@ -2,7 +2,7 @@
use ckb_script::TransactionSnapshot;
use ckb_types::{
- core::{Capacity, Cycle},
+ core::{Capacity, Cycle, EntryCompleted},
packed::Byte32,
};
use std::sync::Arc;
@@ -44,6 +44,15 @@ pub struct Completed {
pub fee: Capacity,
}
+impl From for EntryCompleted {
+ fn from(value: Completed) -> Self {
+ EntryCompleted {
+ cycles: value.cycles,
+ fee: value.fee,
+ }
+ }
+}
+
impl CacheEntry {
/// Constructs a completed CacheEntry
pub fn completed(cycles: Cycle, fee: Capacity) -> Self {