diff --git a/crates/chia-client/src/peer.rs b/crates/chia-client/src/peer.rs index a7db5cc62..fdf665636 100644 --- a/crates/chia-client/src/peer.rs +++ b/crates/chia-client/src/peer.rs @@ -20,6 +20,8 @@ type Requests = Arc>>>; pub enum PeerEvent { CoinStateUpdate(CoinStateUpdate), NewPeakWallet(NewPeakWallet), + MempoolItemsAdded(MempoolItemsAdded), + MempoolItemsRemoved(MempoolItemsRemoved), } pub struct Peer { @@ -64,18 +66,25 @@ impl Peer { &self, network_id: String, node_type: NodeType, + mempool_updates: bool, ) -> Result<(), Error<()>> { + let mut capabilities = vec![ + (1, "1".to_string()), + (2, "1".to_string()), + (3, "1".to_string()), + ]; + + if mempool_updates { + capabilities.push((5, "1".to_string())); + } + let body = Handshake { network_id, protocol_version: "0.0.34".to_string(), software_version: "0.0.0".to_string(), server_port: 0, node_type, - capabilities: vec![ - (1, "1".to_string()), - (2, "1".to_string()), - (3, "1".to_string()), - ], + capabilities, }; self.send(body).await } @@ -350,7 +359,12 @@ impl Peer { } // TODO: Handle unexpected messages. - events!(CoinStateUpdate, NewPeakWallet); + events!( + CoinStateUpdate, + NewPeakWallet, + MempoolItemsAdded, + MempoolItemsRemoved + ); Ok(()) } diff --git a/crates/chia-protocol/src/chia_protocol.rs b/crates/chia-protocol/src/chia_protocol.rs index 28c87264d..a24fe53b8 100644 --- a/crates/chia-protocol/src/chia_protocol.rs +++ b/crates/chia-protocol/src/chia_protocol.rs @@ -135,6 +135,12 @@ pub enum ProtocolMessageTypes { RequestCoinState = 101, RespondCoinState = 102, RejectCoinState = 103, + + // Wallet protocol mempool updates + MempoolItemsAdded = 104, + MempoolItemsRemoved = 105, + RequestCostInfo = 106, + RespondCostInfo = 107, } #[cfg(feature = "py-bindings")] diff --git a/crates/chia-protocol/src/wallet_protocol.rs b/crates/chia-protocol/src/wallet_protocol.rs index 605efd8a5..731edc490 100644 --- a/crates/chia-protocol/src/wallet_protocol.rs +++ b/crates/chia-protocol/src/wallet_protocol.rs @@ -303,3 +303,50 @@ impl chia_traits::ChiaToPython for RejectStateReason { Ok(pyo3::IntoPy::into_py(*self, py).bind(py).clone()) } } + +#[repr(u8)] +#[cfg_attr(feature = "py-bindings", derive(PyJsonDict, PyStreamable))] +#[derive(Streamable, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum MempoolRemoveReason { + Conflict = 1, + BlockInclusion = 2, + PoolFull = 3, + Expired = 4, +} + +#[cfg(feature = "py-bindings")] +impl chia_traits::ChiaToPython for MempoolRemoveReason { + fn to_python<'a>(&self, py: pyo3::Python<'a>) -> pyo3::PyResult> { + Ok(pyo3::IntoPy::into_py(*self, py).bind(py).clone()) + } +} + +#[streamable] +pub struct RemovedMempoolItem { + transaction_id: Bytes32, + reason: MempoolRemoveReason, +} + +#[streamable(message)] +pub struct MempoolItemsAdded { + transaction_ids: Vec, +} + +#[streamable(message)] +pub struct MempoolItemsRemoved { + removed_items: Vec, +} + +#[streamable(message)] +pub struct RequestCostInfo {} + +#[streamable(message)] +pub struct RespondCostInfo { + max_transaction_cost: u64, + max_block_cost: u64, + max_mempool_cost: u64, + mempool_cost: u64, + mempool_fee: u64, + bump_fee_per_cost: u8, +} diff --git a/wheel/python/chia_rs/chia_rs.pyi b/wheel/python/chia_rs/chia_rs.pyi index c6d6b1e57..26332f3ae 100644 --- a/wheel/python/chia_rs/chia_rs.pyi +++ b/wheel/python/chia_rs/chia_rs.pyi @@ -3890,6 +3890,151 @@ class RejectCoinState: def from_json_dict(json_dict: Any) -> RejectCoinState: ... def replace(self, *, reason: Union[ int, _Unspec] = _Unspec()) -> RejectCoinState: ... +class RemovedMempoolItem: + transaction_id: bytes32 + reason: MempoolRemoveReason + def __init__( + self, + transaction_id: bytes, + reason: MempoolRemoveReason + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __richcmp__(self) -> Any: ... + def __deepcopy__(self) -> RemovedMempoolItem: ... + def __copy__(self) -> RemovedMempoolItem: ... + @staticmethod + def from_bytes(bytes) -> RemovedMempoolItem: ... + @staticmethod + def from_bytes_unchecked(bytes) -> RemovedMempoolItem: ... + @staticmethod + def parse_rust(ReadableBuffer, bool = False) -> Tuple[RemovedMempoolItem, int]: ... + def to_bytes(self) -> bytes: ... + def __bytes__(self) -> bytes: ... + def stream_to_bytes(self) -> bytes: ... + def get_hash(self) -> bytes32: ... + def to_json_dict(self) -> Any: ... + @staticmethod + def from_json_dict(json_dict: Any) -> RemovedMempoolItem: ... + def replace(self, *, transaction_id: Union[ bytes32, _Unspec] = _Unspec(), + reason: Union[ MempoolRemoveReason, _Unspec] = _Unspec()) -> RemovedMempoolItem: ... + +class MempoolItemsAdded: + transaction_ids: List[bytes32] + def __init__( + self, + transaction_ids: Sequence[bytes32] + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __richcmp__(self) -> Any: ... + def __deepcopy__(self) -> MempoolItemsAdded: ... + def __copy__(self) -> MempoolItemsAdded: ... + @staticmethod + def from_bytes(bytes) -> MempoolItemsAdded: ... + @staticmethod + def from_bytes_unchecked(bytes) -> MempoolItemsAdded: ... + @staticmethod + def parse_rust(ReadableBuffer, bool = False) -> Tuple[MempoolItemsAdded, int]: ... + def to_bytes(self) -> bytes: ... + def __bytes__(self) -> bytes: ... + def stream_to_bytes(self) -> bytes: ... + def get_hash(self) -> bytes32: ... + def to_json_dict(self) -> Any: ... + @staticmethod + def from_json_dict(json_dict: Any) -> MempoolItemsAdded: ... + def replace(self, *, transaction_ids: Union[ List[bytes32], _Unspec] = _Unspec()) -> MempoolItemsAdded: ... + +class MempoolItemsRemoved: + removed_items: List[RemovedMempoolItem] + def __init__( + self, + removed_items: Sequence[RemovedMempoolItem] + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __richcmp__(self) -> Any: ... + def __deepcopy__(self) -> MempoolItemsRemoved: ... + def __copy__(self) -> MempoolItemsRemoved: ... + @staticmethod + def from_bytes(bytes) -> MempoolItemsRemoved: ... + @staticmethod + def from_bytes_unchecked(bytes) -> MempoolItemsRemoved: ... + @staticmethod + def parse_rust(ReadableBuffer, bool = False) -> Tuple[MempoolItemsRemoved, int]: ... + def to_bytes(self) -> bytes: ... + def __bytes__(self) -> bytes: ... + def stream_to_bytes(self) -> bytes: ... + def get_hash(self) -> bytes32: ... + def to_json_dict(self) -> Any: ... + @staticmethod + def from_json_dict(json_dict: Any) -> MempoolItemsRemoved: ... + def replace(self, *, removed_items: Union[ List[RemovedMempoolItem], _Unspec] = _Unspec()) -> MempoolItemsRemoved: ... + +class RequestCostInfo: + def __init__( + self + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __richcmp__(self) -> Any: ... + def __deepcopy__(self) -> RequestCostInfo: ... + def __copy__(self) -> RequestCostInfo: ... + @staticmethod + def from_bytes(bytes) -> RequestCostInfo: ... + @staticmethod + def from_bytes_unchecked(bytes) -> RequestCostInfo: ... + @staticmethod + def parse_rust(ReadableBuffer, bool = False) -> Tuple[RequestCostInfo, int]: ... + def to_bytes(self) -> bytes: ... + def __bytes__(self) -> bytes: ... + def stream_to_bytes(self) -> bytes: ... + def get_hash(self) -> bytes32: ... + def to_json_dict(self) -> Any: ... + @staticmethod + def from_json_dict(json_dict: Any) -> RequestCostInfo: ... + +class RespondCostInfo: + max_transaction_cost: uint64 + max_block_cost: uint64 + max_mempool_cost: uint64 + mempool_cost: uint64 + mempool_fee: uint64 + bump_fee_per_cost: uint8 + def __init__( + self, + max_transaction_cost: uint64, + max_block_cost: uint64, + max_mempool_cost: uint64, + mempool_cost: uint64, + mempool_fee: uint64, + bump_fee_per_cost: uint8 + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __richcmp__(self) -> Any: ... + def __deepcopy__(self) -> RespondCostInfo: ... + def __copy__(self) -> RespondCostInfo: ... + @staticmethod + def from_bytes(bytes) -> RespondCostInfo: ... + @staticmethod + def from_bytes_unchecked(bytes) -> RespondCostInfo: ... + @staticmethod + def parse_rust(ReadableBuffer, bool = False) -> Tuple[RespondCostInfo, int]: ... + def to_bytes(self) -> bytes: ... + def __bytes__(self) -> bytes: ... + def stream_to_bytes(self) -> bytes: ... + def get_hash(self) -> bytes32: ... + def to_json_dict(self) -> Any: ... + @staticmethod + def from_json_dict(json_dict: Any) -> RespondCostInfo: ... + def replace(self, *, max_transaction_cost: Union[ uint64, _Unspec] = _Unspec(), + max_block_cost: Union[ uint64, _Unspec] = _Unspec(), + max_mempool_cost: Union[ uint64, _Unspec] = _Unspec(), + mempool_cost: Union[ uint64, _Unspec] = _Unspec(), + mempool_fee: Union[ uint64, _Unspec] = _Unspec(), + bump_fee_per_cost: Union[ uint8, _Unspec] = _Unspec()) -> RespondCostInfo: ... + class SubEpochData: reward_chain_hash: bytes32 num_blocks_overflow: uint8