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/generate_type_stubs.py b/wheel/generate_type_stubs.py index fb85366fb..0672be01a 100644 --- a/wheel/generate_type_stubs.py +++ b/wheel/generate_type_stubs.py @@ -7,7 +7,9 @@ input_dir = crates_dir / "chia-protocol" / "src" # enums are exposed to python as int -enums = set(["NodeType", "ProtocolMessageTypes", "RejectStateReason"]) +enums = set( + ["NodeType", "ProtocolMessageTypes", "RejectStateReason", "MempoolRemoveReason"] +) def transform_type(m: str) -> str: diff --git a/wheel/python/chia_rs/chia_rs.pyi b/wheel/python/chia_rs/chia_rs.pyi index 7d1734fa5..7513d7582 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(cls, json_dict: dict[str, Any]) -> Self: ... def replace(self, *, reason: Union[ int, _Unspec] = _Unspec()) -> RejectCoinState: ... +@final +class RemovedMempoolItem: + transaction_id: bytes32 + reason: int + def __init__( + self, + transaction_id: bytes, + reason: int + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __deepcopy__(self, memo: object) -> RemovedMempoolItem: ... + def __copy__(self) -> RemovedMempoolItem: ... + @classmethod + def from_bytes(cls, blob: bytes) -> Self: ... + @classmethod + def from_bytes_unchecked(cls, blob: bytes) -> Self: ... + @classmethod + def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, 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) -> dict[str, Any]: ... + @classmethod + def from_json_dict(cls, json_dict: dict[str, Any]) -> Self: ... + def replace(self, *, transaction_id: Union[ bytes32, _Unspec] = _Unspec(), + reason: Union[ int, _Unspec] = _Unspec()) -> RemovedMempoolItem: ... + +@final +class MempoolItemsAdded: + transaction_ids: list[bytes32] + def __init__( + self, + transaction_ids: Sequence[bytes32] + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __deepcopy__(self, memo: object) -> MempoolItemsAdded: ... + def __copy__(self) -> MempoolItemsAdded: ... + @classmethod + def from_bytes(cls, blob: bytes) -> Self: ... + @classmethod + def from_bytes_unchecked(cls, blob: bytes) -> Self: ... + @classmethod + def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, 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) -> dict[str, Any]: ... + @classmethod + def from_json_dict(cls, json_dict: dict[str, Any]) -> Self: ... + def replace(self, *, transaction_ids: Union[ list[bytes32], _Unspec] = _Unspec()) -> MempoolItemsAdded: ... + +@final +class MempoolItemsRemoved: + removed_items: list[RemovedMempoolItem] + def __init__( + self, + removed_items: Sequence[RemovedMempoolItem] + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __deepcopy__(self, memo: object) -> MempoolItemsRemoved: ... + def __copy__(self) -> MempoolItemsRemoved: ... + @classmethod + def from_bytes(cls, blob: bytes) -> Self: ... + @classmethod + def from_bytes_unchecked(cls, blob: bytes) -> Self: ... + @classmethod + def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, 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) -> dict[str, Any]: ... + @classmethod + def from_json_dict(cls, json_dict: dict[str, Any]) -> Self: ... + def replace(self, *, removed_items: Union[ list[RemovedMempoolItem], _Unspec] = _Unspec()) -> MempoolItemsRemoved: ... + +@final +class RequestCostInfo: + def __init__( + self + ) -> None: ... + def __hash__(self) -> int: ... + def __repr__(self) -> str: ... + def __deepcopy__(self, memo: object) -> RequestCostInfo: ... + def __copy__(self) -> RequestCostInfo: ... + @classmethod + def from_bytes(cls, blob: bytes) -> Self: ... + @classmethod + def from_bytes_unchecked(cls, blob: bytes) -> Self: ... + @classmethod + def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, 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) -> dict[str, Any]: ... + @classmethod + def from_json_dict(cls, json_dict: dict[str, Any]) -> Self: ... + +@final +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 __deepcopy__(self, memo: object) -> RespondCostInfo: ... + def __copy__(self) -> RespondCostInfo: ... + @classmethod + def from_bytes(cls, blob: bytes) -> Self: ... + @classmethod + def from_bytes_unchecked(cls, blob: bytes) -> Self: ... + @classmethod + def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, 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) -> dict[str, Any]: ... + @classmethod + def from_json_dict(cls, json_dict: dict[str, Any]) -> Self: ... + 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: ... + @final class SubEpochData: reward_chain_hash: bytes32 diff --git a/wheel/src/api.rs b/wheel/src/api.rs index 581e1e73d..6ff58c6b9 100644 --- a/wheel/src/api.rs +++ b/wheel/src/api.rs @@ -20,20 +20,21 @@ use chia_protocol::{ BlockRecord, Bytes32, ChallengeBlockInfo, ChallengeChainSubSlot, ClassgroupElement, Coin, CoinSpend, CoinState, CoinStateFilters, CoinStateUpdate, EndOfSubSlotBundle, FeeEstimate, FeeEstimateGroup, FeeRate, Foliage, FoliageBlockData, FoliageTransactionBlock, FullBlock, - Handshake, HeaderBlock, InfusedChallengeChainSubSlot, LazyNode, Message, NewCompactVDF, - NewPeak, NewPeakWallet, NewSignagePointOrEndOfSubSlot, NewTransaction, NewUnfinishedBlock, - NewUnfinishedBlock2, PoolTarget, Program, ProofBlockHeader, ProofOfSpace, - PuzzleSolutionResponse, RecentChainData, RegisterForCoinUpdates, RegisterForPhUpdates, - RejectAdditionsRequest, RejectBlock, RejectBlockHeaders, RejectBlocks, RejectCoinState, - RejectHeaderBlocks, RejectHeaderRequest, RejectPuzzleSolution, RejectPuzzleState, - RejectRemovalsRequest, RequestAdditions, RequestBlock, RequestBlockHeader, RequestBlockHeaders, - RequestBlocks, RequestChildren, RequestCoinState, RequestCompactVDF, RequestFeeEstimates, + Handshake, HeaderBlock, InfusedChallengeChainSubSlot, LazyNode, MempoolItemsAdded, + MempoolItemsRemoved, Message, NewCompactVDF, NewPeak, NewPeakWallet, + NewSignagePointOrEndOfSubSlot, NewTransaction, NewUnfinishedBlock, NewUnfinishedBlock2, + PoolTarget, Program, ProofBlockHeader, ProofOfSpace, PuzzleSolutionResponse, RecentChainData, + RegisterForCoinUpdates, RegisterForPhUpdates, RejectAdditionsRequest, RejectBlock, + RejectBlockHeaders, RejectBlocks, RejectCoinState, RejectHeaderBlocks, RejectHeaderRequest, + RejectPuzzleSolution, RejectPuzzleState, RejectRemovalsRequest, RemovedMempoolItem, + RequestAdditions, RequestBlock, RequestBlockHeader, RequestBlockHeaders, RequestBlocks, + RequestChildren, RequestCoinState, RequestCompactVDF, RequestCostInfo, RequestFeeEstimates, RequestHeaderBlocks, RequestMempoolTransactions, RequestPeers, RequestProofOfWeight, RequestPuzzleSolution, RequestPuzzleState, RequestRemovals, RequestRemoveCoinSubscriptions, RequestRemovePuzzleSubscriptions, RequestSesInfo, RequestSignagePointOrEndOfSubSlot, RequestTransaction, RequestUnfinishedBlock, RequestUnfinishedBlock2, RespondAdditions, RespondBlock, RespondBlockHeader, RespondBlockHeaders, RespondBlocks, RespondChildren, - RespondCoinState, RespondCompactVDF, RespondEndOfSubSlot, RespondFeeEstimates, + RespondCoinState, RespondCompactVDF, RespondCostInfo, RespondEndOfSubSlot, RespondFeeEstimates, RespondHeaderBlocks, RespondPeers, RespondProofOfWeight, RespondPuzzleSolution, RespondPuzzleState, RespondRemovals, RespondRemoveCoinSubscriptions, RespondRemovePuzzleSubscriptions, RespondSesInfo, RespondSignagePoint, RespondToCoinUpdates, @@ -573,6 +574,11 @@ pub fn chia_rs(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; // full node protocol m.add_class::()?; diff --git a/wheel/stubtest.allowlist b/wheel/stubtest.allowlist index 0da900bc1..a9869b980 100644 --- a/wheel/stubtest.allowlist +++ b/wheel/stubtest.allowlist @@ -29,6 +29,9 @@ chia_rs\.PrivateKey\.__init__ # TODO: expects *args chia_rs\.RequestPeers\.__init__ +# TODO: expects *args +chia_rs\.RequestCostInfo\.__init__ + # TODO: ask stubtest/mypy about these as they seem unlikely to be our doing chia_rs\.sized_byte_class\.Iterable chia_rs\.sized_byte_class\.BinaryIO\.write