From 317d51c61af49798db6357b62ae6a719005dd3a3 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 28 Apr 2024 13:45:57 +0300 Subject: [PATCH 01/37] Add basic types --- Cargo.lock | 18 ++ Cargo.toml | 1 + common/src/chain/mod.rs | 2 + common/src/chain/order_id.rs | 74 +++++++++ .../chain/transaction/output/output_value.rs | 7 + orders-accounting/Cargo.toml | 26 +++ orders-accounting/src/cache.rs | 156 ++++++++++++++++++ orders-accounting/src/data.rs | 71 ++++++++ orders-accounting/src/error.rs | 47 ++++++ orders-accounting/src/lib.rs | 39 +++++ orders-accounting/src/operations.rs | 61 +++++++ orders-accounting/src/view.rs | 60 +++++++ 12 files changed, 562 insertions(+) create mode 100644 common/src/chain/order_id.rs create mode 100644 orders-accounting/Cargo.toml create mode 100644 orders-accounting/src/cache.rs create mode 100644 orders-accounting/src/data.rs create mode 100644 orders-accounting/src/error.rs create mode 100644 orders-accounting/src/lib.rs create mode 100644 orders-accounting/src/operations.rs create mode 100644 orders-accounting/src/view.rs diff --git a/Cargo.lock b/Cargo.lock index 8bd2884647..ba39c4811b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4896,6 +4896,24 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "orders-accounting" +version = "0.5.1" +dependencies = [ + "accounting", + "chainstate-types", + "common", + "crypto", + "parity-scale-codec", + "randomness", + "rstest", + "serialization", + "test-utils", + "thiserror", + "utils", + "variant_count", +] + [[package]] name = "ouroboros" version = "0.18.4" diff --git a/Cargo.toml b/Cargo.toml index f23e834e5b..e7987a9a41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "networking", # Pure networking implementations "node-gui", # Node GUI binary. "node-lib", # Node lib; the common library between daemon, tui and gui node executables. + "orders-accounting", # Orders accounting "p2p", # P2p communication interfaces and protocols. "p2p/backend-test-suite", # P2p backend agnostic tests. "p2p/types", # P2p support types with minimal dependencies. diff --git a/common/src/chain/mod.rs b/common/src/chain/mod.rs index 15e4c6a82d..57ac90f0b4 100644 --- a/common/src/chain/mod.rs +++ b/common/src/chain/mod.rs @@ -22,6 +22,7 @@ pub mod tokens; pub mod transaction; mod coin_unit; +mod order_id; mod pos; mod pow; mod upgrades; @@ -34,6 +35,7 @@ pub use coin_unit::CoinUnit; pub use config::ChainConfig; pub use gen_block::{GenBlock, GenBlockId}; pub use genesis::Genesis; +pub use order_id::OrderId; pub use pos::{ config::PoSChainConfig, config_builder::PoSChainConfigBuilder, get_initial_randomness, pos_initial_difficulty, DelegationId, PoSConsensusVersion, PoolId, diff --git a/common/src/chain/order_id.rs b/common/src/chain/order_id.rs new file mode 100644 index 0000000000..c39946cd03 --- /dev/null +++ b/common/src/chain/order_id.rs @@ -0,0 +1,74 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + address::{hexified::HexifiedAddress, traits::Addressable, AddressError}, + chain::ChainConfig, + primitives::{Id, H256}, +}; +use randomness::{CryptoRng, Rng}; +use serialization::{Decode, DecodeAll, Encode}; +use typename::TypeName; + +#[derive(Eq, PartialEq, TypeName)] +pub enum Order {} +pub type OrderId = Id; + +impl OrderId { + pub fn random_using(rng: &mut R) -> Self { + Self::new(H256::random_using(rng)) + } + + pub const fn zero() -> Self { + Self::new(H256::zero()) + } +} + +impl Addressable for OrderId { + type Error = AddressError; + + fn address_prefix(&self, chain_config: &ChainConfig) -> &str { + //chain_config.order_id_address_prefix() + todo!() + } + + fn encode_to_bytes_for_address(&self) -> Vec { + self.encode() + } + + fn decode_from_bytes_from_address>(address_bytes: T) -> Result + where + Self: Sized, + { + Self::decode_all(&mut address_bytes.as_ref()) + .map_err(|e| AddressError::DecodingError(e.to_string())) + } + + fn json_wrapper_prefix() -> &'static str { + "HexifiedOrderId" + } +} + +impl serde::Serialize for OrderId { + fn serialize(&self, serializer: S) -> Result { + HexifiedAddress::serde_serialize(self, serializer) + } +} + +impl<'de> serde::Deserialize<'de> for OrderId { + fn deserialize>(deserializer: D) -> Result { + HexifiedAddress::::serde_deserialize(deserializer) + } +} diff --git a/common/src/chain/transaction/output/output_value.rs b/common/src/chain/transaction/output/output_value.rs index 2dd5a5e277..34bd1ce3f2 100644 --- a/common/src/chain/transaction/output/output_value.rs +++ b/common/src/chain/transaction/output/output_value.rs @@ -37,6 +37,13 @@ impl OutputValue { OutputValue::TokenV0(_) | OutputValue::TokenV1(_, _) => None, } } + + pub fn amount(&self) -> Amount { + match self { + OutputValue::Coin(v) | OutputValue::TokenV1(_, v) => *v, + OutputValue::TokenV0(_) => Amount::ZERO, // FIXME: convenient but suspicious + } + } } impl From for OutputValue { diff --git a/orders-accounting/Cargo.toml b/orders-accounting/Cargo.toml new file mode 100644 index 0000000000..b4c45c76e5 --- /dev/null +++ b/orders-accounting/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "orders-accounting" +license.workspace = true +version.workspace = true +edition.workspace = true +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +accounting = { path = "../accounting" } +chainstate-types = { path = "../chainstate/types" } +common = { path = "../common" } +crypto = { path = "../crypto" } +randomness = { path = "../randomness" } +serialization = { path = "../serialization" } +utils = { path = "../utils" } + +thiserror.workspace = true +parity-scale-codec.workspace = true +variant_count.workspace = true + +[dev-dependencies] +test-utils = { path = "../test-utils" } + +rstest.workspace = true diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs new file mode 100644 index 0000000000..cc13a38895 --- /dev/null +++ b/orders-accounting/src/cache.rs @@ -0,0 +1,156 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use common::{ + chain::{output_value::OutputValue, OrderId}, + primitives::Amount, +}; +use utils::ensure; + +use crate::{ + data::{OrderData, OrdersAccountingDeltaData}, + error::{Error, Result}, + operations::{ + CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, OrdersAccountingUndo, + }, + view::OrdersAccountingView, +}; + +pub struct OrdersAccountingCache

{ + parent: P, + data: OrdersAccountingDeltaData, +} + +impl OrdersAccountingCache

{ + pub fn new(parent: P) -> Self { + Self { + parent, + data: OrdersAccountingDeltaData::new(), + } + } + + pub fn consume(self) -> OrdersAccountingDeltaData { + self.data + } + + pub fn data(&self) -> &OrdersAccountingDeltaData { + &self.data + } +} + +impl OrdersAccountingView for OrdersAccountingCache

{ + type Error = Error; + + fn get_order_data(&self, id: &OrderId) -> Result> { + todo!() + } + + fn get_ask_balance(&self, id: &OrderId) -> Result> { + todo!() + } + + fn get_give_balance(&self, id: &OrderId) -> Result> { + todo!() + } +} + +impl OrdersAccountingOperations for OrdersAccountingCache

{ + fn create_order(&mut self, id: OrderId, data: OrderData) -> Result { + if self.get_order_data(&id)?.is_some() { + return Err(Error::OrderAlreadyExists(id)); + } + + if self.get_ask_balance(&id)?.is_some() { + return Err(Error::OrderAlreadyExists(id)); + } + + // FIXME: ask type != give ? + let ask_value = data.ask.clone(); + let give_value = data.give.clone(); + let undo_data = self + .data + .order_data + .merge_delta_data_element(id, accounting::DataDelta::new(None, Some(data)))?; + + self.data.ask_balances.add_unsigned(id, ask_value.amount())?; + self.data.give_balances.add_unsigned(id, give_value.amount())?; + + Ok(OrdersAccountingUndo::CreateOrder(CreateOrderUndo { + id, + undo_data, + })) + } + + fn fill_order(&mut self, id: OrderId, fill_value: OutputValue) -> Result { + let order_data = self.get_order_data(&id)?.ok_or(Error::OrderDataNotFound(id))?; + let give_balance = + self.get_give_balance(&id)?.ok_or(Error::OrderGiveBalanceNotFound(id))?; + let ask_balance = self.get_ask_balance(&id)?.ok_or(Error::OrderAskBalanceNotFound(id))?; + + { + let ask_balance = match order_data.ask { + OutputValue::Coin(_) => OutputValue::Coin(ask_balance), + OutputValue::TokenV0(_) => return Err(Error::UnsupportedTokenVersion), + OutputValue::TokenV1(token_id, _) => OutputValue::TokenV1(token_id, ask_balance), + }; + + // FIXME: given_value > ask_balance should be possible + ensure_currencies_and_amounts_match(id, &ask_balance, &fill_value)?; + } + + let filled_amount = calculate_filled_amount(ask_balance, give_balance, fill_value.amount()) + .ok_or(Error::OrderOverflow(id))?; + + self.data.give_balances.sub_unsigned(id, filled_amount)?; + self.data.ask_balances.sub_unsigned(id, fill_value.amount())?; + + Ok(OrdersAccountingUndo::FillOrder(FillOrderUndo { + id, + sub_ask_value: fill_value.amount(), + sub_give_value: filled_amount, + })) + } + + fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()> { + todo!() + } +} + +fn calculate_filled_amount(ask: Amount, give: Amount, fill: Amount) -> Option { + (give * fill.into_atoms()).and_then(|v| v / ask.into_atoms()) +} + +fn ensure_currencies_and_amounts_match( + order_id: OrderId, + left: &OutputValue, + right: &OutputValue, +) -> Result<()> { + match (left, right) { + (OutputValue::Coin(amount1), OutputValue::Coin(amount2)) => { + ensure!(amount1 >= amount2, Error::OrderOverflow(order_id)); + Ok(()) + } + (OutputValue::TokenV1(id1, amount1), OutputValue::TokenV1(id2, amount2)) => { + ensure!(amount1 >= amount2, Error::OrderOverflow(order_id)); + ensure!(id1 == id2, Error::CurrencyMismatch); + Ok(()) + } + (OutputValue::Coin(_), OutputValue::TokenV1(_, _)) + | (OutputValue::TokenV1(_, _), OutputValue::Coin(_)) => Err(Error::CurrencyMismatch), + (OutputValue::TokenV0(_), _) | (_, OutputValue::TokenV0(_)) => { + Err(Error::UnsupportedTokenVersion) + } + } +} diff --git a/orders-accounting/src/data.rs b/orders-accounting/src/data.rs new file mode 100644 index 0000000000..d5cfb8b2f7 --- /dev/null +++ b/orders-accounting/src/data.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::BTreeMap; + +use accounting::{DeltaAmountCollection, DeltaDataCollection, DeltaDataUndoCollection}; +use common::{ + chain::{output_value::OutputValue, Destination, OrderId}, + primitives::Amount, +}; +use serialization::{Decode, Encode}; + +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct OrderData { + pub authority: Destination, + pub ask: OutputValue, + pub give: OutputValue, +} + +#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)] +pub struct OrdersAccountingData { + pub order_data: BTreeMap, + pub ask_balances: BTreeMap, + pub give_balances: BTreeMap, +} + +impl OrdersAccountingData { + pub fn new() -> Self { + Self { + order_data: BTreeMap::new(), + ask_balances: BTreeMap::new(), + give_balances: BTreeMap::new(), + } + } +} + +#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)] +pub struct OrdersAccountingDeltaData { + pub(crate) order_data: DeltaDataCollection, + pub(crate) ask_balances: DeltaAmountCollection, + pub(crate) give_balances: DeltaAmountCollection, +} + +impl OrdersAccountingDeltaData { + pub fn new() -> Self { + Self { + order_data: DeltaDataCollection::new(), + ask_balances: DeltaAmountCollection::new(), + give_balances: DeltaAmountCollection::new(), + } + } +} + +#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)] +pub struct OrdersAccountingDeltaUndoData { + pub(crate) order_data: DeltaDataUndoCollection, + pub(crate) ask_balances: DeltaAmountCollection, + pub(crate) give_balances: DeltaAmountCollection, +} diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs new file mode 100644 index 0000000000..2244603083 --- /dev/null +++ b/orders-accounting/src/error.rs @@ -0,0 +1,47 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use common::chain::OrderId; + +#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] +pub enum Error { + #[error("Accounting storage error")] + StorageError(#[from] chainstate_types::storage_result::Error), + #[error("Base accounting error: {0}")] + AccountingError(#[from] accounting::Error), + #[error("Order already exist: `{0}`")] + OrderAlreadyExists(OrderId), + #[error("Data for order {0}` not found")] + OrderDataNotFound(OrderId), + #[error("Ask balance for order {0}` not found")] + OrderAskBalanceNotFound(OrderId), + #[error("Give balance for order {0}` not found")] + OrderGiveBalanceNotFound(OrderId), + #[error("Unsupported token version")] + UnsupportedTokenVersion, + #[error("Coin type mismatch")] + CurrencyMismatch, + #[error("Order overflow: `{0}`")] + OrderOverflow(OrderId), + + // TODO Need a more granular error reporting in the following + // https://github.com/mintlayer/mintlayer-core/issues/811 + #[error("Orders accounting view query failed")] + ViewFail, + #[error("Orders accounting storage write failed")] + StorageWrite, +} + +pub type Result = core::result::Result; diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs new file mode 100644 index 0000000000..231da6a1d4 --- /dev/null +++ b/orders-accounting/src/lib.rs @@ -0,0 +1,39 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod cache; +mod data; +mod error; +mod operations; +//mod storage; +mod view; + +//pub use { +// cache::TokensAccountingCache, +// data::{ +// FungibleTokenData, TokenData, TokensAccountingData, TokensAccountingDeltaData, +// TokensAccountingDeltaUndoData, +// }, +// error::Error, +// operations::{random_undo_for_test, TokenAccountingUndo, TokensAccountingOperations}, +// storage::{ +// db::TokensAccountingDB, in_memory::InMemoryTokensAccounting, TokensAccountingStorageRead, +// TokensAccountingStorageWrite, +// }, +// view::{FlushableTokensAccountingView, TokensAccountingView}, +//}; + +//#[cfg(test)] +//mod tests; diff --git a/orders-accounting/src/operations.rs b/orders-accounting/src/operations.rs new file mode 100644 index 0000000000..2fbd5d7be4 --- /dev/null +++ b/orders-accounting/src/operations.rs @@ -0,0 +1,61 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use accounting::DataDeltaUndo; +use common::{ + chain::{output_value::OutputValue, OrderId}, + primitives::Amount, +}; +use serialization::{Decode, Encode}; +use variant_count::VariantCount; + +use crate::{data::OrderData, error::Result}; + +#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] +pub struct CreateOrderUndo { + pub(crate) id: OrderId, + pub(crate) undo_data: DataDeltaUndo, +} + +#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] +pub struct CancelOrderUndo { + pub(crate) id: OrderId, + pub(crate) undo_data: DataDeltaUndo, + pub(crate) ask_value: OutputValue, + pub(crate) give_value: OutputValue, +} + +#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] +pub struct FillOrderUndo { + pub(crate) id: OrderId, + pub(crate) sub_ask_value: Amount, + pub(crate) sub_give_value: Amount, +} + +#[must_use] +#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, VariantCount)] +pub enum OrdersAccountingUndo { + CreateOrder(CreateOrderUndo), + CancelOrder(CancelOrderUndo), + FillOrder(FillOrderUndo), +} + +pub trait OrdersAccountingOperations { + fn create_order(&mut self, id: OrderId, data: OrderData) -> Result; + + fn fill_order(&mut self, id: OrderId, value: OutputValue) -> Result; + + fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()>; +} diff --git a/orders-accounting/src/view.rs b/orders-accounting/src/view.rs new file mode 100644 index 0000000000..363557ac3a --- /dev/null +++ b/orders-accounting/src/view.rs @@ -0,0 +1,60 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::ops::Deref; + +use common::{chain::OrderId, primitives::Amount}; + +use crate::data::{OrderData, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}; + +pub trait OrdersAccountingView { + /// Error that can occur during queries + type Error: std::error::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error>; + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error>; + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error>; +} + +pub trait FlushableOrdersAccountingView { + /// Errors potentially triggered by flushing the view + type Error: std::error::Error; + + /// Performs bulk modification + fn batch_write_orders_data( + &mut self, + delta: OrdersAccountingDeltaData, + ) -> Result; +} + +impl OrdersAccountingView for T +where + T: Deref, + ::Target: OrdersAccountingView, +{ + type Error = ::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + self.deref().get_order_data(id) + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.deref().get_ask_balance(id) + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.deref().get_give_balance(id) + } +} From 7e4f88665fb4a6969f67d76126bc99f6e5f9fee4 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Mon, 29 Apr 2024 13:55:30 +0300 Subject: [PATCH 02/37] Pass PoSAccountingView to ConstraitsAccumulator --- .../scanner-lib/src/blockchain_state/mod.rs | 73 ++++++++++---- .../src/accounts_balances_tracker.rs | 19 ++-- .../src/constraints_accumulator.rs | 45 ++++----- .../src/tests/constraints_tests.rs | 96 ++++++++++++------- .../src/tests/homomorphism.rs | 23 +++-- .../input_output_policy/mod.rs | 21 +--- 6 files changed, 166 insertions(+), 111 deletions(-) diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 3ff5d4c971..17fe470ed5 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -672,6 +672,56 @@ async fn token_decimals( Ok((token_id, decimals)) } +struct PoSAccountingAdapterToCheckFees { + pools: BTreeMap, +} + +impl PoSAccountingView for PoSAccountingAdapterToCheckFees { + type Error = pos_accounting::Error; + + fn pool_exists(&self, _pool_id: PoolId) -> Result { + unimplemented!() + } + + fn get_pool_balance(&self, _pool_id: PoolId) -> Result, Self::Error> { + unimplemented!() + } + + fn get_pool_data(&self, pool_id: PoolId) -> Result, Self::Error> { + Ok(self.pools.get(&pool_id).cloned()) + } + + fn get_pool_delegations_shares( + &self, + _pool_id: PoolId, + ) -> Result>, Self::Error> { + unimplemented!() + } + + fn get_delegation_balance( + &self, + _delegation_id: DelegationId, + ) -> Result, Self::Error> { + // only used for checks for attempted to print money but we don't need to check that here + Ok(Some(Amount::MAX)) + } + + fn get_delegation_data( + &self, + _delegation_id: DelegationId, + ) -> Result, Self::Error> { + unimplemented!() + } + + fn get_pool_delegation_share( + &self, + _pool_id: PoolId, + _delegation_id: DelegationId, + ) -> Result, Self::Error> { + unimplemented!() + } +} + async fn tx_fees( chain_config: &ChainConfig, block_height: BlockHeight, @@ -680,17 +730,13 @@ async fn tx_fees( new_outputs: &BTreeMap, ) -> Result { let inputs_utxos = collect_inputs_utxos(db_tx, tx.inputs(), new_outputs).await?; - let pools = prefetch_pool_amounts(&inputs_utxos, db_tx).await?; - - let staker_balance_getter = |pool_id: PoolId| Ok(pools.get(&pool_id).cloned()); - // only used for checks for attempted to print money but we don't need to check that here - let delegation_balance_getter = |_delegation_id: DelegationId| Ok(Some(Amount::MAX)); + let pools = prefetch_pool_data(&inputs_utxos, db_tx).await?; + let pos_accounting_adapter = PoSAccountingAdapterToCheckFees { pools }; let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( chain_config, block_height, - staker_balance_getter, - delegation_balance_getter, + &pos_accounting_adapter, tx.inputs(), &inputs_utxos, ) @@ -703,23 +749,18 @@ async fn tx_fees( Ok(consumed_accumulator) } -async fn prefetch_pool_amounts( +async fn prefetch_pool_data( inputs_utxos: &Vec>, db_tx: &mut T, -) -> Result, ApiServerStorageError> { +) -> Result, ApiServerStorageError> { let mut pools = BTreeMap::new(); for output in inputs_utxos { match output { Some( TxOutput::CreateStakePool(pool_id, _) | TxOutput::ProduceBlockFromStake(_, pool_id), ) => { - let amount = db_tx - .get_pool_data(*pool_id) - .await? - .expect("should exist") - .staker_balance() - .expect("no overflow"); - pools.insert(*pool_id, amount); + let data = db_tx.get_pool_data(*pool_id).await?.expect("should exist"); + pools.insert(*pool_id, data); } Some( TxOutput::Burn(_) diff --git a/chainstate/constraints-value-accumulator/src/accounts_balances_tracker.rs b/chainstate/constraints-value-accumulator/src/accounts_balances_tracker.rs index 626e0dc23b..0472675d42 100644 --- a/chainstate/constraints-value-accumulator/src/accounts_balances_tracker.rs +++ b/chainstate/constraints-value-accumulator/src/accounts_balances_tracker.rs @@ -19,6 +19,7 @@ use common::{ chain::{AccountSpending, AccountType, DelegationId}, primitives::Amount, }; +use pos_accounting::PoSAccountingView; use crate::Error; @@ -43,20 +44,17 @@ impl From for AccountType { } } -pub struct AccountsBalancesTracker<'a, DelegationBalanceGetterFn> { +pub struct AccountsBalancesTracker

{ balances: BTreeMap, - delegation_balance_getter: &'a DelegationBalanceGetterFn, + pos_accounting_view: P, } -impl<'a, DelegationBalanceGetterFn> AccountsBalancesTracker<'a, DelegationBalanceGetterFn> -where - DelegationBalanceGetterFn: Fn(DelegationId) -> Result, Error>, -{ - pub fn new(delegation_balance_getter: &'a DelegationBalanceGetterFn) -> Self { +impl AccountsBalancesTracker

{ + pub fn new(pos_accounting_view: P) -> Self { Self { balances: BTreeMap::new(), - delegation_balance_getter, + pos_accounting_view, } } @@ -65,7 +63,10 @@ where Entry::Vacant(e) => { let (balance, spending) = match account { AccountSpending::DelegationBalance(id, spending) => { - let balance = (self.delegation_balance_getter)(id)? + let balance = self + .pos_accounting_view + .get_delegation_balance(id) + .map_err(|_| pos_accounting::Error::ViewFail)? .ok_or(Error::AccountBalanceNotFound(account.clone().into()))?; (balance, spending) } diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 484d429023..94bcd38503 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -18,10 +18,11 @@ use std::{collections::BTreeMap, num::NonZeroU64}; use common::{ chain::{ output_value::OutputValue, timelock::OutputTimeLock, AccountCommand, AccountSpending, - ChainConfig, DelegationId, PoolId, TxInput, TxOutput, UtxoOutPoint, + ChainConfig, TxInput, TxOutput, UtxoOutPoint, }, primitives::{Amount, BlockHeight, CoinOrTokenId, Fee, Subsidy}, }; +use pos_accounting::PoSAccountingView; use utils::ensure; use crate::accounts_balances_tracker::AccountsBalancesTracker; @@ -55,18 +56,13 @@ impl ConstrainedValueAccumulator { }) } - pub fn from_inputs( + pub fn from_inputs( chain_config: &ChainConfig, block_height: BlockHeight, - staker_balance_getter: StakerBalanceGetterFn, - delegation_balance_getter: DelegationBalanceGetterFn, + pos_accounting_view: &impl PoSAccountingView, inputs: &[TxInput], inputs_utxos: &[Option], - ) -> Result - where - StakerBalanceGetterFn: Fn(PoolId) -> Result, Error>, - DelegationBalanceGetterFn: Fn(DelegationId) -> Result, Error>, - { + ) -> Result { ensure!( inputs.len() == inputs_utxos.len(), Error::InputsAndInputsUtxosLengthMismatch(inputs.len(), inputs_utxos.len()) @@ -74,8 +70,7 @@ impl ConstrainedValueAccumulator { let mut accumulator = Self::new(); let mut total_fee_deducted = Amount::ZERO; - let mut accounts_balances_tracker = - AccountsBalancesTracker::new(&delegation_balance_getter); + let mut accounts_balances_tracker = AccountsBalancesTracker::new(pos_accounting_view); for (input, input_utxo) in inputs.iter().zip(inputs_utxos.iter()) { match input { @@ -85,7 +80,7 @@ impl ConstrainedValueAccumulator { accumulator.process_input_utxo( chain_config, block_height, - &staker_balance_getter, + pos_accounting_view, outpoint.clone(), input_utxo, )?; @@ -121,17 +116,14 @@ impl ConstrainedValueAccumulator { Ok(accumulator) } - fn process_input_utxo( + fn process_input_utxo( &mut self, chain_config: &ChainConfig, block_height: BlockHeight, - staker_balance_getter: &StakerBalanceGetterFn, + pos_accounting_view: &impl PoSAccountingView, outpoint: UtxoOutPoint, input_utxo: &TxOutput, - ) -> Result<(), Error> - where - StakerBalanceGetterFn: Fn(PoolId) -> Result, Error>, - { + ) -> Result<(), Error> { match input_utxo { TxOutput::Transfer(value, _) | TxOutput::LockThenTransfer(value, _, _) @@ -167,8 +159,14 @@ impl ConstrainedValueAccumulator { insert_or_increase(&mut self.unconstrained_value, CoinOrTokenId::Coin, *coins)?; } TxOutput::CreateStakePool(pool_id, _) | TxOutput::ProduceBlockFromStake(_, pool_id) => { - let staker_balance = staker_balance_getter(*pool_id)? + let staker_balance = pos_accounting_view + .get_pool_data(*pool_id) + .map_err(|_| pos_accounting::Error::ViewFail)? + .map(|pool_data| pool_data.staker_balance()) + .transpose() + .map_err(Error::PoSAccountingError)? .ok_or(Error::PledgeAmountNotFound(*pool_id))?; + let maturity_distance = chain_config.staking_pool_spend_maturity_block_count(block_height); @@ -195,16 +193,13 @@ impl ConstrainedValueAccumulator { Ok(()) } - fn process_input_account( + fn process_input_account( &mut self, chain_config: &ChainConfig, block_height: BlockHeight, account: &AccountSpending, - accounts_balances_tracker: &mut AccountsBalancesTracker, - ) -> Result<(), Error> - where - DelegationBalanceGetterFn: Fn(DelegationId) -> Result, Error>, - { + accounts_balances_tracker: &mut AccountsBalancesTracker

, + ) -> Result<(), Error> { match account { AccountSpending::DelegationBalance(_, spend_amount) => { accounts_balances_tracker.spend_from_account(account.clone())?; diff --git a/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs b/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs index 6e3df89c1c..5964b1c889 100644 --- a/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs +++ b/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::BTreeMap; + use common::{ chain::{ config::ChainType, output_value::OutputValue, stakelock::StakePoolData, @@ -25,6 +27,7 @@ use common::{ }, }; use crypto::vrf::{VRFKeyKind, VRFPrivateKey}; +use pos_accounting::{InMemoryPoSAccounting, PoSAccountingDB, PoolData}; use randomness::{CryptoRng, Rng}; use rstest::rstest; use test_utils::random::{make_seedable_rng, Seed}; @@ -63,8 +66,14 @@ fn allow_fees_from_decommission(#[case] seed: Seed) { let fee_atoms = rng.gen_range(1..100); let stake_pool_data = create_stake_pool_data(&mut rng, staked_atoms); - let pledge_getter = |_| Ok(Some(Amount::from_atoms(staked_atoms))); - let delegation_balance_getter = |_| Ok(None); + let pos_store = InMemoryPoSAccounting::from_values( + BTreeMap::from_iter([(pool_id, PoolData::from(stake_pool_data.clone()))]), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::new(), + ); + let pos_db = PoSAccountingDB::new(&pos_store); let inputs = vec![TxInput::Utxo(UtxoOutPoint::new( OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), @@ -84,8 +93,7 @@ fn allow_fees_from_decommission(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &input_utxos, ) @@ -122,8 +130,14 @@ fn allow_fees_from_spend_share(#[case] seed: Seed) { let delegated_atoms = rng.gen_range(100..1000); let fee_atoms = rng.gen_range(1..100); - let pledge_getter = |_| Ok(None); - let delegation_balance_getter = |_| Ok(Some(Amount::from_atoms(delegated_atoms))); + let pos_store = InMemoryPoSAccounting::from_values( + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::from_iter([(delegation_id, Amount::from_atoms(delegated_atoms))]), + BTreeMap::new(), + ); + let pos_db = PoSAccountingDB::new(&pos_store); let inputs_utxos = vec![None]; let inputs = vec![TxInput::from_account( @@ -140,8 +154,7 @@ fn allow_fees_from_spend_share(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -180,8 +193,14 @@ fn no_timelock_outputs_on_decommission(#[case] seed: Seed) { let less_than_staked_amount = Amount::from_atoms(rng.gen_range(1..staked_atoms)); let stake_pool_data = create_stake_pool_data(&mut rng, staked_atoms); - let pledge_getter = |_| Ok(Some(Amount::from_atoms(staked_atoms))); - let delegation_balance_getter = |_| Ok(None); + let pos_store = InMemoryPoSAccounting::from_values( + BTreeMap::from_iter([(pool_id, PoolData::from(stake_pool_data.clone()))]), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::new(), + ); + let pos_db = PoSAccountingDB::new(&pos_store); let inputs = vec![ TxInput::from_utxo( @@ -214,8 +233,7 @@ fn no_timelock_outputs_on_decommission(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -242,8 +260,7 @@ fn no_timelock_outputs_on_decommission(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -279,8 +296,14 @@ fn try_to_unlock_coins_with_smaller_timelock(#[case] seed: Seed) { let less_than_staked_amount = Amount::from_atoms(rng.gen_range(1..staked_atoms)); let stake_pool_data = create_stake_pool_data(&mut rng, staked_atoms); - let pledge_getter = |_| Ok(Some(Amount::from_atoms(staked_atoms))); - let delegation_balance_getter = |_| Ok(None); + let pos_store = InMemoryPoSAccounting::from_values( + BTreeMap::from_iter([(pool_id, PoolData::from(stake_pool_data.clone()))]), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::new(), + ); + let pos_db = PoSAccountingDB::new(&pos_store); let inputs = vec![ TxInput::from_utxo( @@ -323,8 +346,7 @@ fn try_to_unlock_coins_with_smaller_timelock(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -362,8 +384,7 @@ fn try_to_unlock_coins_with_smaller_timelock(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -413,8 +434,14 @@ fn check_timelock_saturation(#[case] seed: Seed) { let transferred_atoms = rng.gen_range(100..1000); - let pledge_getter = |_| Ok(Some(Amount::from_atoms(staked_atoms))); - let delegation_balance_getter = |_| Ok(Some(Amount::from_atoms(delegated_atoms))); + let pos_store = InMemoryPoSAccounting::from_values( + BTreeMap::from_iter([(pool_id, PoolData::from(stake_pool_data.clone()))]), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::from_iter([(delegation_id, Amount::from_atoms(delegated_atoms))]), + BTreeMap::new(), + ); + let pos_db = PoSAccountingDB::new(&pos_store); let inputs = vec![ TxInput::from_utxo( @@ -457,8 +484,7 @@ fn check_timelock_saturation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -489,8 +515,7 @@ fn check_timelock_saturation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -523,8 +548,14 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { let delegation_balance = Amount::from_atoms(rng.gen_range(100..1000)); let overspent_amount = (delegation_balance + Amount::from_atoms(1)).unwrap(); - let pledge_getter = |_| Ok(None); - let delegation_balance_getter = |_| Ok(Some(delegation_balance)); + let pos_store = InMemoryPoSAccounting::from_values( + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::from_iter([(delegation_id, delegation_balance)]), + BTreeMap::new(), + ); + let pos_db = PoSAccountingDB::new(&pos_store); // it's an error to spend more the balance let inputs = vec![TxInput::from_account( @@ -537,8 +568,7 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ); @@ -568,8 +598,7 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -605,8 +634,7 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) diff --git a/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs b/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs index 3f417e73d1..2699e52947 100644 --- a/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs +++ b/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs @@ -58,6 +58,10 @@ fn create_stake_pool_data(rng: &mut (impl Rng + CryptoRng), atoms_to_stake: u128 #[trace] #[case(Seed::from_entropy())] fn accumulators_homomorphism(#[case] seed: Seed) { + use std::collections::BTreeMap; + + use pos_accounting::{InMemoryPoSAccounting, PoSAccountingDB, PoolData}; + let mut rng = make_seedable_rng(seed); let chain_config = common::chain::config::Builder::new(ChainType::Mainnet) @@ -88,8 +92,14 @@ fn accumulators_homomorphism(#[case] seed: Seed) { - spend_share_output, )); - let pledge_getter = |_| Ok(Some(Amount::from_atoms(staked_atoms))); - let delegation_balance_getter = |_| Ok(Some(delegation_balance)); + let pos_store = InMemoryPoSAccounting::from_values( + BTreeMap::from_iter([(pool_id, PoolData::from(stake_pool_data.clone()))]), + BTreeMap::new(), + BTreeMap::new(), + BTreeMap::from_iter([(delegation_id, delegation_balance)]), + BTreeMap::new(), + ); + let pos_db = PoSAccountingDB::new(&pos_store); let (decommission_tx, decommission_tx_inputs_utxos) = { let decommission_pool_utxo = if rng.gen::() { @@ -211,8 +221,7 @@ fn accumulators_homomorphism(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, &inputs, &inputs_utxos, ) @@ -235,8 +244,7 @@ fn accumulators_homomorphism(#[case] seed: Seed) { let decommission_inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, decommission_tx.inputs(), &decommission_tx_inputs_utxos, ) @@ -252,8 +260,7 @@ fn accumulators_homomorphism(#[case] seed: Seed) { let spend_share_inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - pledge_getter, - delegation_balance_getter, + &pos_db, spend_share_tx.inputs(), &spend_share_inputs_utxos, ) diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs index 76db7eea87..cc9bf9095f 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs @@ -19,8 +19,7 @@ use common::{ output_value::OutputValue, signature::Signable, tokens::{get_tokens_issuance_count, TokenId}, - Block, ChainConfig, DelegationId, PoolId, TokenIssuanceVersion, Transaction, TxInput, - TxOutput, + Block, ChainConfig, TokenIssuanceVersion, Transaction, TxInput, TxOutput, }, primitives::{Amount, BlockHeight, Fee, Id, Idable, Subsidy}, }; @@ -181,26 +180,10 @@ pub fn check_tx_inputs_outputs_policy( }) .collect::, ConnectTransactionError>>()?; - let staker_balance_getter = |pool_id: PoolId| { - pos_accounting_view - .get_pool_data(pool_id) - .map_err(|_| pos_accounting::Error::ViewFail)? - .map(|pool_data| pool_data.staker_balance()) - .transpose() - .map_err(constraints_value_accumulator::Error::PoSAccountingError) - }; - - let delegation_balance_getter = |delegation_id: DelegationId| { - Ok(pos_accounting_view - .get_delegation_balance(delegation_id) - .map_err(|_| pos_accounting::Error::ViewFail)?) - }; - let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( chain_config, block_height, - staker_balance_getter, - delegation_balance_getter, + pos_accounting_view, tx.inputs(), &inputs_utxos, ) From 2e202e8703b3d2298fd276cfbd466477ed2d1584 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Mon, 29 Apr 2024 17:30:41 +0300 Subject: [PATCH 03/37] Add storage in orders-accounting --- orders-accounting/src/lib.rs | 2 +- orders-accounting/src/storage/db.rs | 108 +++++++++++++++++ orders-accounting/src/storage/in_memory.rs | 128 +++++++++++++++++++++ orders-accounting/src/storage/mod.rs | 91 +++++++++++++++ 4 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 orders-accounting/src/storage/db.rs create mode 100644 orders-accounting/src/storage/in_memory.rs create mode 100644 orders-accounting/src/storage/mod.rs diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index 231da6a1d4..ba429317d3 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -17,7 +17,7 @@ mod cache; mod data; mod error; mod operations; -//mod storage; +mod storage; mod view; //pub use { diff --git a/orders-accounting/src/storage/db.rs b/orders-accounting/src/storage/db.rs new file mode 100644 index 0000000000..7dee314b65 --- /dev/null +++ b/orders-accounting/src/storage/db.rs @@ -0,0 +1,108 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{collections::BTreeMap, ops::Neg}; + +use accounting::{ + combine_amount_delta, combine_data_with_delta, DeltaAmountCollection, DeltaDataUndoCollection, +}; +use common::{chain::OrderId, primitives::Amount}; + +use crate::{ + data::{OrderData, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}, + error::Error, + view::{FlushableOrdersAccountingView, OrdersAccountingView}, +}; + +use super::{OrdersAccountingStorageRead, OrdersAccountingStorageWrite}; + +#[must_use] +pub struct OrdersAccountingDB(S); + +impl OrdersAccountingDB { + pub fn new(store: S) -> Self { + Self(store) + } +} + +impl OrdersAccountingView for OrdersAccountingDB { + type Error = S::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + self.0.get_order_data(id) + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.0.get_ask_balance(id) + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.0.get_give_balance(id) + } +} + +impl OrdersAccountingStorageRead for OrdersAccountingDB { + type Error = S::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + self.0.get_order_data(id) + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.0.get_ask_balance(id) + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.0.get_give_balance(id) + } +} + +impl OrdersAccountingStorageWrite for OrdersAccountingDB { + fn set_order_data(&mut self, id: &OrderId, data: &OrderData) -> Result<(), Self::Error> { + self.0.set_order_data(id, data) + } + + fn del_order_data(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.0.del_order_data(id) + } + + fn set_ask_balance(&mut self, id: &OrderId, balance: &Amount) -> Result<(), Self::Error> { + self.0.set_ask_balance(id, balance) + } + + fn del_ask_balance(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.0.del_ask_balance(id) + } + + fn set_give_balance(&mut self, id: &OrderId, balance: &Amount) -> Result<(), Self::Error> { + self.0.set_give_balance(id, balance) + } + + fn del_give_balance(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.0.del_give_balance(id) + } +} + +impl FlushableOrdersAccountingView for OrdersAccountingDB { + type Error = Error; + + fn batch_write_orders_data( + &mut self, + delta: OrdersAccountingDeltaData, + ) -> Result { + //self.merge_with_delta(delta).log_err().map_err(|_| Error::StorageWrite) + todo!() + } +} diff --git a/orders-accounting/src/storage/in_memory.rs b/orders-accounting/src/storage/in_memory.rs new file mode 100644 index 0000000000..7e427c99cb --- /dev/null +++ b/orders-accounting/src/storage/in_memory.rs @@ -0,0 +1,128 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::BTreeMap; + +use common::{chain::OrderId, primitives::Amount}; + +use crate::{data::OrderData, view::OrdersAccountingView}; + +use super::{OrdersAccountingStorageRead, OrdersAccountingStorageWrite}; + +#[must_use] +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct InMemoryOrdersAccounting { + orders_data: BTreeMap, + ask_balances: BTreeMap, + give_balances: BTreeMap, +} + +impl InMemoryOrdersAccounting { + pub fn new() -> Self { + Self { + orders_data: Default::default(), + ask_balances: Default::default(), + give_balances: Default::default(), + } + } + + pub fn from_values( + orders_data: BTreeMap, + ask_balances: BTreeMap, + give_balances: BTreeMap, + ) -> Self { + Self { + orders_data, + ask_balances, + give_balances, + } + } + + pub fn orders_data(&self) -> &BTreeMap { + &self.orders_data + } + + pub fn ask_balances(&self) -> &BTreeMap { + &self.ask_balances + } + + pub fn give_balances(&self) -> &BTreeMap { + &self.give_balances + } +} + +impl OrdersAccountingStorageRead for InMemoryOrdersAccounting { + type Error = chainstate_types::storage_result::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + Ok(self.orders_data.get(id).cloned()) + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + Ok(self.ask_balances.get(id).cloned()) + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + Ok(self.give_balances.get(id).cloned()) + } +} + +impl OrdersAccountingStorageWrite for InMemoryOrdersAccounting { + fn set_order_data(&mut self, id: &OrderId, data: &OrderData) -> Result<(), Self::Error> { + self.orders_data.insert(*id, data.clone()); + Ok(()) + } + + fn del_order_data(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.orders_data.remove(id); + Ok(()) + } + + fn set_ask_balance(&mut self, id: &OrderId, balance: &Amount) -> Result<(), Self::Error> { + self.ask_balances.insert(*id, *balance); + Ok(()) + } + + fn del_ask_balance(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.ask_balances.remove(id); + Ok(()) + } + + fn set_give_balance(&mut self, id: &OrderId, balance: &Amount) -> Result<(), Self::Error> { + self.give_balances.insert(*id, *balance); + Ok(()) + } + + fn del_give_balance(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.give_balances.remove(id); + Ok(()) + } +} + +impl OrdersAccountingView for InMemoryOrdersAccounting { + type Error = chainstate_types::storage_result::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + Ok(self.orders_data.get(id).cloned()) + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + Ok(self.ask_balances.get(id).cloned()) + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + Ok(self.give_balances.get(id).cloned()) + } +} diff --git a/orders-accounting/src/storage/mod.rs b/orders-accounting/src/storage/mod.rs new file mode 100644 index 0000000000..16dc2600d5 --- /dev/null +++ b/orders-accounting/src/storage/mod.rs @@ -0,0 +1,91 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use common::{chain::OrderId, primitives::Amount}; +use std::ops::{Deref, DerefMut}; + +use crate::data::OrderData; + +pub mod db; +pub mod in_memory; + +pub trait OrdersAccountingStorageRead { + type Error: std::error::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error>; + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error>; + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error>; +} + +pub trait OrdersAccountingStorageWrite: OrdersAccountingStorageRead { + fn set_order_data(&mut self, id: &OrderId, data: &OrderData) -> Result<(), Self::Error>; + fn del_order_data(&mut self, id: &OrderId) -> Result<(), Self::Error>; + + fn set_ask_balance(&mut self, id: &OrderId, balance: &Amount) -> Result<(), Self::Error>; + fn del_ask_balance(&mut self, id: &OrderId) -> Result<(), Self::Error>; + + fn set_give_balance(&mut self, id: &OrderId, balance: &Amount) -> Result<(), Self::Error>; + fn del_give_balance(&mut self, id: &OrderId) -> Result<(), Self::Error>; +} + +impl OrdersAccountingStorageRead for T +where + T: Deref, + ::Target: OrdersAccountingStorageRead, +{ + type Error = ::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + self.deref().get_order_data(id) + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.deref().get_ask_balance(id) + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.deref().get_give_balance(id) + } +} + +impl OrdersAccountingStorageWrite for T +where + T: DerefMut, + ::Target: OrdersAccountingStorageWrite, +{ + fn set_order_data(&mut self, id: &OrderId, data: &OrderData) -> Result<(), Self::Error> { + self.deref_mut().set_order_data(id, data) + } + + fn del_order_data(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.deref_mut().del_order_data(id) + } + + fn set_ask_balance(&mut self, id: &OrderId, balance: &Amount) -> Result<(), Self::Error> { + self.deref_mut().set_ask_balance(id, balance) + } + + fn del_ask_balance(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.deref_mut().del_ask_balance(id) + } + + fn set_give_balance(&mut self, id: &OrderId, balance: &Amount) -> Result<(), Self::Error> { + self.deref_mut().set_give_balance(id, balance) + } + + fn del_give_balance(&mut self, id: &OrderId) -> Result<(), Self::Error> { + self.deref_mut().del_give_balance(id) + } +} From 6e28bedfc9fe1578d13eb2b16033a4a5e5655d34 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Mon, 29 Apr 2024 17:51:41 +0300 Subject: [PATCH 04/37] Withdraw command initial impl --- Cargo.lock | 4 +++ api-server/scanner-lib/Cargo.toml | 1 + .../scanner-lib/src/blockchain_state/mod.rs | 7 ++++ .../scanner-lib/src/sync/tests/simulation.rs | 36 +++++++++++-------- api-server/web-server/src/api/json_helpers.rs | 1 + chainstate/Cargo.toml | 1 + .../constraints-value-accumulator/Cargo.toml | 1 + .../src/constraints_accumulator.rs | 32 ++++++++++++++++- .../src/error.rs | 2 ++ .../src/tests/constraints_tests.rs | 36 +++++++++++++++++++ .../src/tests/homomorphism.rs | 8 +++++ chainstate/src/detail/ban_score.rs | 1 + chainstate/src/detail/error_classification.rs | 1 + chainstate/src/rpc/types/account.rs | 1 + .../test-framework/src/random_tx_maker.rs | 1 + .../src/signature_destination_getter.rs | 1 + chainstate/tx-verifier/Cargo.toml | 1 + .../input_output_policy/mod.rs | 3 ++ .../input_output_policy/purposes_check.rs | 1 + .../tests/constraints_tests.rs | 33 ++++++++++++++--- .../src/transaction_verifier/mod.rs | 6 ++++ common/src/chain/tokens/tokens_utils.rs | 3 +- .../src/chain/transaction/account_outpoint.rs | 7 +++- common/src/primitives/mod.rs | 12 ++++++- mempool/src/pool/entry.rs | 1 + mintscript/src/translate.rs | 2 ++ orders-accounting/src/lib.rs | 6 ++++ pos-accounting/src/lib.rs | 2 +- wallet/src/account/mod.rs | 3 ++ wallet/src/account/output_cache/mod.rs | 7 ++++ 30 files changed, 198 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba39c4811b..a1ba59eb1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,6 +272,7 @@ dependencies = [ "logging", "mempool", "node-comm", + "orders-accounting", "pos-accounting", "randomness", "rstest", @@ -1163,6 +1164,7 @@ dependencies = [ "mockall", "num", "oneshot", + "orders-accounting", "parity-scale-codec", "pos-accounting", "randomness", @@ -1573,6 +1575,7 @@ version = "0.5.1" dependencies = [ "common", "crypto", + "orders-accounting", "pos-accounting", "randomness", "rstest", @@ -8000,6 +8003,7 @@ dependencies = [ "logging", "mintscript", "mockall", + "orders-accounting", "pos-accounting", "randomness", "replace_with", diff --git a/api-server/scanner-lib/Cargo.toml b/api-server/scanner-lib/Cargo.toml index 5ba90996ac..57c435093e 100644 --- a/api-server/scanner-lib/Cargo.toml +++ b/api-server/scanner-lib/Cargo.toml @@ -15,6 +15,7 @@ constraints-value-accumulator = { path = "../../chainstate/constraints-value-acc logging = { path = "../../logging" } mempool = { path = "../../mempool" } node-comm = { path = "../../wallet/wallet-node-client" } +orders-accounting = { path = "../../orders-accounting" } pos-accounting = { path = "../../pos-accounting" } randomness = { path = "../../randomness" } utils = { path = "../../utils" } diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 17fe470ed5..05918b79df 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -614,6 +614,7 @@ async fn calculate_fees( | AccountCommand::UnfreezeToken(token_id) | AccountCommand::LockTokenSupply(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) => Some(*token_id), + AccountCommand::WithdrawOrder(_) => todo!(), }, }) .collect(); @@ -733,9 +734,14 @@ async fn tx_fees( let pools = prefetch_pool_data(&inputs_utxos, db_tx).await?; let pos_accounting_adapter = PoSAccountingAdapterToCheckFees { pools }; + // FIXME: proper impl + let orders_store = orders_accounting::InMemoryOrdersAccounting::new(); + let orders_db = orders_accounting::OrdersAccountingDB::new(&orders_store); + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( chain_config, block_height, + &orders_db, &pos_accounting_adapter, tx.inputs(), &inputs_utxos, @@ -1096,6 +1102,7 @@ async fn update_tables_from_transaction_inputs( ) .await; } + AccountCommand::WithdrawOrder(_) => todo!(), }, TxInput::Account(outpoint) => { match outpoint.account() { diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index ebbf2dd89e..7d05a22972 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -152,6 +152,10 @@ async fn simulation( let mut delegations = BTreeSet::new(); let mut token_ids = BTreeSet::new(); + // FIXME: proper impl + let orders_store = orders_accounting::InMemoryOrdersAccounting::new(); + let orders_db = orders_accounting::OrdersAccountingDB::new(&orders_store); + let mut data_per_block_height = BTreeMap::new(); data_per_block_height.insert( BlockHeight::zero(), @@ -384,6 +388,7 @@ async fn simulation( chain_config.token_change_authority_fee(block_height); burn_coins(&mut statistics, token_change_authority_fee); } + AccountCommand::WithdrawOrder(_) => todo!(), }, }); } @@ -415,25 +420,28 @@ async fn simulation( }) .collect(); - let staker_balance_getter = |pool_id: PoolId| { - Ok(Some( - tf.chainstate - .get_stake_pool_data(pool_id) - .unwrap() - .unwrap() - .staker_balance() - .unwrap(), - )) - }; + //let staker_balance_getter = |pool_id: PoolId| { + // Ok(Some( + // tf.chainstate + // .get_stake_pool_data(pool_id) + // .unwrap() + // .unwrap() + // .staker_balance() + // .unwrap(), + // )) + //}; // only used for checks for attempted to print money but we don't need to check that here - let delegation_balance_getter = - |_delegation_id: DelegationId| Ok(Some(Amount::MAX)); + //let delegation_balance_getter = + // |_delegation_id: DelegationId| Ok(Some(Amount::MAX)); + // FIXME: proper impl + let pos_store = pos_accounting::InMemoryPoSAccounting::new(); + let pos_db = pos_accounting::PoSAccountingDB::new(&pos_store); let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, - staker_balance_getter, - delegation_balance_getter, + &orders_db, + &pos_db, tx.inputs(), &inputs_utxos, ) diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index cf77b95a7a..fef874e798 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -333,6 +333,7 @@ pub fn tx_input_to_json(inp: &TxInput, chain_config: &ChainConfig) -> serde_json "nonce": nonce, }) } + AccountCommand::WithdrawOrder(_) => todo!(), }, } } diff --git a/chainstate/Cargo.toml b/chainstate/Cargo.toml index 5316243241..adc79aca10 100644 --- a/chainstate/Cargo.toml +++ b/chainstate/Cargo.toml @@ -15,6 +15,7 @@ constraints-value-accumulator = { path = "./constraints-value-accumulator" } crypto = { path = "../crypto" } logging = { path = "../logging" } mintscript = { path = "../mintscript" } +orders-accounting = { path = "../orders-accounting" } pos-accounting = { path = "../pos-accounting" } randomness = { path = "../randomness" } rpc = { path = "../rpc" } diff --git a/chainstate/constraints-value-accumulator/Cargo.toml b/chainstate/constraints-value-accumulator/Cargo.toml index 37263ee904..bd41e3388d 100644 --- a/chainstate/constraints-value-accumulator/Cargo.toml +++ b/chainstate/constraints-value-accumulator/Cargo.toml @@ -10,6 +10,7 @@ rust-version.workspace = true [dependencies] common = { path = "../../common" } crypto = { path = "../../crypto" } +orders-accounting = { path = "../../orders-accounting" } pos-accounting = { path = "../../pos-accounting" } randomness = { path = "../../randomness" } utils = { path = "../../utils" } diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 94bcd38503..6fc054691a 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -18,10 +18,11 @@ use std::{collections::BTreeMap, num::NonZeroU64}; use common::{ chain::{ output_value::OutputValue, timelock::OutputTimeLock, AccountCommand, AccountSpending, - ChainConfig, TxInput, TxOutput, UtxoOutPoint, + AccountType, ChainConfig, TxInput, TxOutput, UtxoOutPoint, }, primitives::{Amount, BlockHeight, CoinOrTokenId, Fee, Subsidy}, }; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use utils::ensure; @@ -59,6 +60,7 @@ impl ConstrainedValueAccumulator { pub fn from_inputs( chain_config: &ChainConfig, block_height: BlockHeight, + orders_accounting_view: &impl OrdersAccountingView, pos_accounting_view: &impl PoSAccountingView, inputs: &[TxInput], inputs_utxos: &[Option], @@ -98,6 +100,7 @@ impl ConstrainedValueAccumulator { chain_config, block_height, command, + orders_accounting_view, )?; total_fee_deducted = (total_fee_deducted + fee_to_deduct) @@ -235,6 +238,7 @@ impl ConstrainedValueAccumulator { chain_config: &ChainConfig, block_height: BlockHeight, command: &AccountCommand, + orders_accounting_view: &impl OrdersAccountingView, ) -> Result { match command { AccountCommand::MintTokens(token_id, amount) => { @@ -254,6 +258,32 @@ impl ConstrainedValueAccumulator { AccountCommand::ChangeTokenAuthority(_, _) => { Ok(chain_config.token_change_authority_fee(block_height)) } + AccountCommand::WithdrawOrder(id) => { + let order_data = orders_accounting_view + .get_order_data(id) + .map_err(|_| orders_accounting::Error::ViewFail)? + .ok_or(orders_accounting::Error::OrderDataNotFound(*id))?; + let ask_balance = orders_accounting_view + .get_ask_balance(id) + .map_err(|_| orders_accounting::Error::ViewFail)? + .ok_or(orders_accounting::Error::OrderAskBalanceNotFound(*id))?; + let give_balance = orders_accounting_view + .get_give_balance(id) + .map_err(|_| orders_accounting::Error::ViewFail)? + .ok_or(orders_accounting::Error::OrderGiveBalanceNotFound(*id))?; + + let initially_asked = order_data.ask.amount(); + let ask_amount = (initially_asked - ask_balance) + .ok_or(Error::NegativeAccountBalance(AccountType::Order(*id)))?; + + let ask_id = CoinOrTokenId::from_output_value(order_data.ask).expect("cannot fail"); + insert_or_increase(&mut self.unconstrained_value, ask_id, ask_amount)?; + + let give_id = + CoinOrTokenId::from_output_value(order_data.give).expect("cannot fail"); + insert_or_increase(&mut self.unconstrained_value, give_id, give_balance)?; + Ok(Amount::ZERO) + } } } diff --git a/chainstate/constraints-value-accumulator/src/error.rs b/chainstate/constraints-value-accumulator/src/error.rs index 03371dd2f0..e43e94d310 100644 --- a/chainstate/constraints-value-accumulator/src/error.rs +++ b/chainstate/constraints-value-accumulator/src/error.rs @@ -38,6 +38,8 @@ pub enum Error { MissingOutputOrSpent(UtxoOutPoint), #[error("PoS accounting error: `{0}`")] PoSAccountingError(#[from] pos_accounting::Error), + #[error("Orders accounting error: `{0}`")] + OrdersAccountingError(#[from] orders_accounting::Error), #[error("Pledge amount not found for pool: `{0}`")] PledgeAmountNotFound(PoolId), #[error("Spending non-spendable output: `{0:?}`")] diff --git a/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs b/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs index 5964b1c889..b4aed820d8 100644 --- a/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs +++ b/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs @@ -27,6 +27,7 @@ use common::{ }, }; use crypto::vrf::{VRFKeyKind, VRFPrivateKey}; +use orders_accounting::{InMemoryOrdersAccounting, OrderData, OrdersAccountingDB}; use pos_accounting::{InMemoryPoSAccounting, PoSAccountingDB, PoolData}; use randomness::{CryptoRng, Rng}; use rstest::rstest; @@ -75,6 +76,10 @@ fn allow_fees_from_decommission(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let inputs = vec![TxInput::Utxo(UtxoOutPoint::new( OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), 0, @@ -93,6 +98,7 @@ fn allow_fees_from_decommission(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &input_utxos, @@ -139,6 +145,10 @@ fn allow_fees_from_spend_share(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let inputs_utxos = vec![None]; let inputs = vec![TxInput::from_account( AccountNonce::new(0), @@ -154,6 +164,7 @@ fn allow_fees_from_spend_share(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -202,6 +213,10 @@ fn no_timelock_outputs_on_decommission(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let inputs = vec![ TxInput::from_utxo( OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), @@ -233,6 +248,7 @@ fn no_timelock_outputs_on_decommission(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -260,6 +276,7 @@ fn no_timelock_outputs_on_decommission(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -305,6 +322,10 @@ fn try_to_unlock_coins_with_smaller_timelock(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let inputs = vec![ TxInput::from_utxo( OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), @@ -346,6 +367,7 @@ fn try_to_unlock_coins_with_smaller_timelock(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -384,6 +406,7 @@ fn try_to_unlock_coins_with_smaller_timelock(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -443,6 +466,10 @@ fn check_timelock_saturation(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let inputs = vec![ TxInput::from_utxo( OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), @@ -484,6 +511,7 @@ fn check_timelock_saturation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -515,6 +543,7 @@ fn check_timelock_saturation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -557,6 +586,10 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + // it's an error to spend more the balance let inputs = vec![TxInput::from_account( AccountNonce::new(0), @@ -568,6 +601,7 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -598,6 +632,7 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -634,6 +669,7 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, diff --git a/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs b/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs index 2699e52947..c14b5bc50f 100644 --- a/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs +++ b/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs @@ -60,6 +60,7 @@ fn create_stake_pool_data(rng: &mut (impl Rng + CryptoRng), atoms_to_stake: u128 fn accumulators_homomorphism(#[case] seed: Seed) { use std::collections::BTreeMap; + use orders_accounting::{InMemoryOrdersAccounting, OrdersAccountingDB}; use pos_accounting::{InMemoryPoSAccounting, PoSAccountingDB, PoolData}; let mut rng = make_seedable_rng(seed); @@ -101,6 +102,10 @@ fn accumulators_homomorphism(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let (decommission_tx, decommission_tx_inputs_utxos) = { let decommission_pool_utxo = if rng.gen::() { TxOutput::CreateStakePool(pool_id, Box::new(stake_pool_data)) @@ -221,6 +226,7 @@ fn accumulators_homomorphism(#[case] seed: Seed) { let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, &inputs, &inputs_utxos, @@ -244,6 +250,7 @@ fn accumulators_homomorphism(#[case] seed: Seed) { let decommission_inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, decommission_tx.inputs(), &decommission_tx_inputs_utxos, @@ -260,6 +267,7 @@ fn accumulators_homomorphism(#[case] seed: Seed) { let spend_share_inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, + &orders_db, &pos_db, spend_share_tx.inputs(), &spend_share_inputs_utxos, diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 0987e8b143..a79be69d62 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -583,6 +583,7 @@ impl BanScore for constraints_value_accumulator::Error { constraints_value_accumulator::Error::DelegationBalanceNotFound(_) => 0, constraints_value_accumulator::Error::AccountBalanceNotFound(_) => 0, constraints_value_accumulator::Error::NegativeAccountBalance(_) => 100, + constraints_value_accumulator::Error::OrdersAccountingError(_) => todo!(), } } } diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index bbed9511cf..7f04b1752a 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -874,6 +874,7 @@ impl BlockProcessingErrorClassification for constraints_value_accumulator::Error | Error::NegativeAccountBalance(_) => BlockProcessingErrorClass::BadBlock, Error::PoSAccountingError(err) => err.classify(), + Error::OrdersAccountingError(_) => todo!(), } } } diff --git a/chainstate/src/rpc/types/account.rs b/chainstate/src/rpc/types/account.rs index c4c138441d..f86ba1cd49 100644 --- a/chainstate/src/rpc/types/account.rs +++ b/chainstate/src/rpc/types/account.rs @@ -103,6 +103,7 @@ impl RpcAccountCommand { new_authority: RpcAddress::new(chain_config, destination.clone())?, } } + AccountCommand::WithdrawOrder(_) => todo!(), }; Ok(result) } diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index c482c4bd94..34fb6df4e8 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -409,6 +409,7 @@ impl<'a> RandomTxMaker<'a> { result_inputs.extend(inputs); result_outputs.extend(outputs); } + AccountType::Order(_) => todo!(), } } diff --git a/chainstate/test-framework/src/signature_destination_getter.rs b/chainstate/test-framework/src/signature_destination_getter.rs index 66400a6fe5..bdf0bb7ce0 100644 --- a/chainstate/test-framework/src/signature_destination_getter.rs +++ b/chainstate/test-framework/src/signature_destination_getter.rs @@ -160,6 +160,7 @@ impl<'a> SignatureDestinationGetter<'a> { }; Ok(destination) } + AccountCommand::WithdrawOrder(_) => todo!(), }, } }; diff --git a/chainstate/tx-verifier/Cargo.toml b/chainstate/tx-verifier/Cargo.toml index cecd9b41a4..9407bd7486 100644 --- a/chainstate/tx-verifier/Cargo.toml +++ b/chainstate/tx-verifier/Cargo.toml @@ -17,6 +17,7 @@ constraints-value-accumulator = { path = "../constraints-value-accumulator" } crypto = { path = "../../crypto" } logging = { path = "../../logging" } mintscript = { path = "../../mintscript" } +orders-accounting = { path = "../../orders-accounting" } pos-accounting = { path = "../../pos-accounting" } randomness = { path = "../../randomness" } serialization = { path = "../../serialization" } diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs index cc9bf9095f..0e3cca0187 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs @@ -24,6 +24,7 @@ use common::{ primitives::{Amount, BlockHeight, Fee, Id, Idable, Subsidy}, }; use constraints_value_accumulator::{AccumulatedFee, ConstrainedValueAccumulator}; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use thiserror::Error; @@ -145,6 +146,7 @@ pub fn check_tx_inputs_outputs_policy( tx: &Transaction, chain_config: &ChainConfig, block_height: BlockHeight, + orders_accounting_view: &impl OrdersAccountingView, pos_accounting_view: &impl PoSAccountingView, utxo_view: &impl utxo::UtxosView, ) -> Result { @@ -183,6 +185,7 @@ pub fn check_tx_inputs_outputs_policy( let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( chain_config, block_height, + orders_accounting_view, pos_accounting_view, tx.inputs(), &inputs_utxos, diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs index eeb3a98f58..f60b5e9f56 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs @@ -184,6 +184,7 @@ pub fn check_tx_inputs_outputs_purposes( }) .count(); + // FIXME: allow tokens and order commands? ensure!( account_commands_count <= 1, IOPolicyError::MultipleAccountCommands diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/constraints_tests.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/constraints_tests.rs index 28d20fe0f8..b698f35411 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/constraints_tests.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/constraints_tests.rs @@ -24,6 +24,7 @@ use common::{ primitives::{per_thousand::PerThousand, Amount, CoinOrTokenId, H256}, }; use crypto::vrf::{VRFKeyKind, VRFPrivateKey}; +use orders_accounting::{InMemoryOrdersAccounting, OrdersAccountingDB}; use randomness::{CryptoRng, Rng, SliceRandom}; use rstest::rstest; use test_utils::{ @@ -101,6 +102,10 @@ fn timelock_constraints_on_decommission_in_tx(#[case] seed: Seed) { ); let pos_db = pos_accounting::PoSAccountingDB::new(&pos_store); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let decommission_pool_utxo = if rng.gen::() { TxOutput::CreateStakePool(pool_id, Box::new(stake_pool_data)) } else { @@ -158,6 +163,7 @@ fn timelock_constraints_on_decommission_in_tx(#[case] seed: Seed) { &tx, &chain_config, BlockHeight::new(1), + &orders_db, &pos_db, &utxo_db, ) @@ -205,8 +211,15 @@ fn timelock_constraints_on_decommission_in_tx(#[case] seed: Seed) { let (utxo_db, tx) = prepare_utxos_and_tx(&mut rng, input_utxos, outputs); - check_tx_inputs_outputs_policy(&tx, &chain_config, BlockHeight::new(1), &pos_db, &utxo_db) - .unwrap(); + check_tx_inputs_outputs_policy( + &tx, + &chain_config, + BlockHeight::new(1), + &orders_db, + &pos_db, + &utxo_db, + ) + .unwrap(); } } @@ -240,6 +253,10 @@ fn timelock_constraints_on_spend_share_in_tx(#[case] seed: Seed) { let pos_db = pos_accounting::PoSAccountingDB::new(&pos_store); let utxo_db = UtxosDBInMemoryImpl::new(Id::::new(H256::zero()), BTreeMap::new()); + // FIXME: proper impl + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + // make timelock outputs but total atoms that locked is less than required { let random_additional_value = rng.gen_range(1..=atoms_to_spend); @@ -284,6 +301,7 @@ fn timelock_constraints_on_spend_share_in_tx(#[case] seed: Seed) { &tx, &chain_config, BlockHeight::new(1), + &orders_db, &pos_db, &utxo_db, ) @@ -336,7 +354,14 @@ fn timelock_constraints_on_spend_share_in_tx(#[case] seed: Seed) { ) .unwrap(); - check_tx_inputs_outputs_policy(&tx, &chain_config, BlockHeight::new(1), &pos_db, &utxo_db) - .unwrap(); + check_tx_inputs_outputs_policy( + &tx, + &chain_config, + BlockHeight::new(1), + &orders_db, + &pos_db, + &utxo_db, + ) + .unwrap(); } } diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 2e8e8339ba..5c414c010c 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -583,6 +583,7 @@ where }); Some(res) } + AccountCommand::WithdrawOrder(_) => todo!(), }, }) .collect::, _>>()?; @@ -725,11 +726,16 @@ where .map_err(|_| ConnectTransactionError::TxVerifierStorage) })?; + // FIXME: proper impl + let orders_store = orders_accounting::InMemoryOrdersAccounting::new(); + let orders_db = orders_accounting::OrdersAccountingDB::new(&orders_store); + // check for attempted money printing and invalid inputs/outputs combinations let fee = input_output_policy::check_tx_inputs_outputs_policy( tx.transaction(), self.chain_config.as_ref(), tx_source.expected_block_height(), + &orders_db, &self.pos_accounting_adapter.accounting_delta(), &self.utxo_cache, )?; diff --git a/common/src/chain/tokens/tokens_utils.rs b/common/src/chain/tokens/tokens_utils.rs index 4b266714fb..cd8e8882e9 100644 --- a/common/src/chain/tokens/tokens_utils.rs +++ b/common/src/chain/tokens/tokens_utils.rs @@ -54,7 +54,8 @@ pub fn get_token_supply_change_count(inputs: &[TxInput]) -> usize { TxInput::AccountCommand(_, op) => match op { AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) - | AccountCommand::ChangeTokenAuthority(_, _) => false, + | AccountCommand::ChangeTokenAuthority(_, _) + | AccountCommand::WithdrawOrder(_) => false, AccountCommand::MintTokens(_, _) | AccountCommand::UnmintTokens(_) | AccountCommand::LockTokenSupply(_) => true, diff --git a/common/src/chain/transaction/account_outpoint.rs b/common/src/chain/transaction/account_outpoint.rs index 79a3b6751c..3fe3cefdb1 100644 --- a/common/src/chain/transaction/account_outpoint.rs +++ b/common/src/chain/transaction/account_outpoint.rs @@ -16,7 +16,7 @@ use crate::{ chain::{ tokens::{IsTokenUnfreezable, TokenId}, - AccountNonce, DelegationId, + AccountNonce, DelegationId, OrderId, }, primitives::Amount, }; @@ -32,6 +32,8 @@ pub enum AccountType { /// Token account type is used to authorize changes in token data. #[codec(index = 1)] Token(TokenId), + #[codec(index = 1)] + Order(OrderId), } impl From for AccountType { @@ -51,6 +53,7 @@ impl From for AccountType { | AccountCommand::FreezeToken(id, _) | AccountCommand::UnfreezeToken(id) | AccountCommand::ChangeTokenAuthority(id, _) => AccountType::Token(id), + AccountCommand::WithdrawOrder(id) => AccountType::Order(id), } } } @@ -110,6 +113,8 @@ pub enum AccountCommand { // Change the authority who can authorize operations for a token #[codec(index = 5)] ChangeTokenAuthority(TokenId, Destination), + #[codec(index = 6)] + WithdrawOrder(OrderId), } /// Type of OutPoint that represents spending from an account diff --git a/common/src/primitives/mod.rs b/common/src/primitives/mod.rs index e3756a8fb3..70f89546b0 100644 --- a/common/src/primitives/mod.rs +++ b/common/src/primitives/mod.rs @@ -35,7 +35,7 @@ pub use height::{BlockCount, BlockDistance, BlockHeight}; pub use id::{Id, Idable, H256}; pub use version_tag::VersionTag; -use crate::chain::tokens::TokenId; +use crate::chain::{output_value::OutputValue, tokens::TokenId}; use serialization::{Decode, Encode}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -49,3 +49,13 @@ pub enum CoinOrTokenId { Coin, TokenId(TokenId), } + +impl CoinOrTokenId { + pub fn from_output_value(value: OutputValue) -> Option { + match value { + OutputValue::Coin(_) => Some(Self::Coin), + OutputValue::TokenV0(_) => None, + OutputValue::TokenV1(id, _) => Some(Self::TokenId(id)), + } + } +} diff --git a/mempool/src/pool/entry.rs b/mempool/src/pool/entry.rs index 0c0e0d3d5e..e9d6b98d83 100644 --- a/mempool/src/pool/entry.rs +++ b/mempool/src/pool/entry.rs @@ -74,6 +74,7 @@ impl TxDependency { | AccountCommand::ChangeTokenAuthority(_, _) => { Self::TokenSupplyAccount(TxAccountDependency::new(acct.clone().into(), nonce)) } + AccountCommand::WithdrawOrder(_) => todo!(), } } diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index 7bdaa1e0ca..ec87a4408a 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -205,6 +205,7 @@ impl TranslateInput for SignedTransaction { }; Ok(checksig(dest)) } + AccountCommand::WithdrawOrder(_) => todo!(), }, } } @@ -294,6 +295,7 @@ impl TranslateInput for TimelockOnly { | AccountCommand::FreezeToken(_token_id, _) | AccountCommand::UnfreezeToken(_token_id) | AccountCommand::ChangeTokenAuthority(_token_id, _) => Ok(WitnessScript::TRUE), + AccountCommand::WithdrawOrder(_) => Ok(WitnessScript::TRUE), }, } } diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index ba429317d3..f94c7b2f66 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -20,6 +20,12 @@ mod operations; mod storage; mod view; +pub use { + data::OrderData, + error::Error, + storage::{db::OrdersAccountingDB, in_memory::InMemoryOrdersAccounting}, + view::OrdersAccountingView, +}; //pub use { // cache::TokensAccountingCache, // data::{ diff --git a/pos-accounting/src/lib.rs b/pos-accounting/src/lib.rs index 3c191298c2..d4a3278e2b 100644 --- a/pos-accounting/src/lib.rs +++ b/pos-accounting/src/lib.rs @@ -20,7 +20,7 @@ mod storage; pub use crate::{ data::PoSAccountingData, - error::{Error, Result}, + error::Error, pool::{ delegation::DelegationData, delta::{data::PoSAccountingDeltaData, DeltaMergeUndo, PoSAccountingDelta}, diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index 28e42b250f..cab6c8c068 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1345,6 +1345,7 @@ impl Account { .token_data(token_id) .map(|data| (None, Some(data.authority.clone()))) .ok_or(WalletError::UnknownTokenId(*token_id)), + AccountCommand::WithdrawOrder(_) => todo!(), } } }) @@ -1822,6 +1823,7 @@ impl Account { self.find_token(token_id).is_ok() || self.is_destination_mine_or_watched(address) } + AccountCommand::WithdrawOrder(_) => todo!(), }, }); let relevant_outputs = self.mark_outputs_as_seen(db_tx, tx.outputs())?; @@ -2228,6 +2230,7 @@ fn group_preselected_inputs( .ok_or(WalletError::OutputAmountOverflow)?, )?; } + AccountCommand::WithdrawOrder(_) => todo!(), }, } } diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index 314745b096..f641358cbc 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -670,6 +670,7 @@ impl OutputCache { | AccountCommand::ChangeTokenAuthority(_, _) | AccountCommand::UnfreezeToken(_) => None, AccountCommand::FreezeToken(frozen_token_id, _) => Some(frozen_token_id), + AccountCommand::WithdrawOrder(_) => todo!(), }, }); @@ -727,6 +728,7 @@ impl OutputCache { | AccountCommand::UnfreezeToken(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) | AccountCommand::UnmintTokens(token_id) => frozen_token_id == token_id, + AccountCommand::WithdrawOrder(_) => todo!(), }, TxInput::Account(_) => false, }) @@ -917,6 +919,7 @@ impl OutputCache { self.token_issuance.insert(*token_id, data); } } + AccountCommand::WithdrawOrder(_) => todo!(), }, } } @@ -1017,6 +1020,7 @@ impl OutputCache { data.unconfirmed_txs.remove(tx_id); } } + AccountCommand::WithdrawOrder(_) => todo!(), }, } } @@ -1305,6 +1309,7 @@ impl OutputCache { data.unconfirmed_txs.remove(&tx_id.into()); } } + AccountCommand::WithdrawOrder(_) => todo!(), }, } } @@ -1509,6 +1514,7 @@ fn apply_freeze_mutations_from_tx( | AccountCommand::UnmintTokens(_) | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} + AccountCommand::WithdrawOrder(_) => todo!(), }, } } @@ -1548,6 +1554,7 @@ fn apply_total_supply_mutations_from_tx( AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} + AccountCommand::WithdrawOrder(_) => todo!(), }, } } From 2b2c7a9dbb0d068eb2e673ccb6504843e378a1b2 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 30 Apr 2024 09:12:24 +0300 Subject: [PATCH 05/37] Move OrderData to common --- .../src/tests/constraints_tests.rs | 2 +- common/src/chain/mod.rs | 4 ++-- common/src/chain/{order_id.rs => order.rs} | 9 +++++++++ orders-accounting/src/cache.rs | 4 ++-- orders-accounting/src/data.rs | 9 +-------- orders-accounting/src/lib.rs | 1 - orders-accounting/src/operations.rs | 4 ++-- orders-accounting/src/storage/db.rs | 7 +++++-- orders-accounting/src/storage/in_memory.rs | 7 +++++-- orders-accounting/src/storage/mod.rs | 7 ++++--- orders-accounting/src/view.rs | 7 +++++-- 11 files changed, 36 insertions(+), 25 deletions(-) rename common/src/chain/{order_id.rs => order.rs} (89%) diff --git a/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs b/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs index b4aed820d8..9cd6a55159 100644 --- a/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs +++ b/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs @@ -27,7 +27,7 @@ use common::{ }, }; use crypto::vrf::{VRFKeyKind, VRFPrivateKey}; -use orders_accounting::{InMemoryOrdersAccounting, OrderData, OrdersAccountingDB}; +use orders_accounting::{InMemoryOrdersAccounting, OrdersAccountingDB}; use pos_accounting::{InMemoryPoSAccounting, PoSAccountingDB, PoolData}; use randomness::{CryptoRng, Rng}; use rstest::rstest; diff --git a/common/src/chain/mod.rs b/common/src/chain/mod.rs index 57ac90f0b4..f149dd3877 100644 --- a/common/src/chain/mod.rs +++ b/common/src/chain/mod.rs @@ -22,7 +22,7 @@ pub mod tokens; pub mod transaction; mod coin_unit; -mod order_id; +mod order; mod pos; mod pow; mod upgrades; @@ -35,7 +35,7 @@ pub use coin_unit::CoinUnit; pub use config::ChainConfig; pub use gen_block::{GenBlock, GenBlockId}; pub use genesis::Genesis; -pub use order_id::OrderId; +pub use order::{OrderData, OrderId}; pub use pos::{ config::PoSChainConfig, config_builder::PoSChainConfigBuilder, get_initial_randomness, pos_initial_difficulty, DelegationId, PoSConsensusVersion, PoolId, diff --git a/common/src/chain/order_id.rs b/common/src/chain/order.rs similarity index 89% rename from common/src/chain/order_id.rs rename to common/src/chain/order.rs index c39946cd03..772b80032a 100644 --- a/common/src/chain/order_id.rs +++ b/common/src/chain/order.rs @@ -22,6 +22,8 @@ use randomness::{CryptoRng, Rng}; use serialization::{Decode, DecodeAll, Encode}; use typename::TypeName; +use super::{output_value::OutputValue, Destination}; + #[derive(Eq, PartialEq, TypeName)] pub enum Order {} pub type OrderId = Id; @@ -72,3 +74,10 @@ impl<'de> serde::Deserialize<'de> for OrderId { HexifiedAddress::::serde_deserialize(deserializer) } } + +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] +pub struct OrderData { + pub authority: Destination, + pub ask: OutputValue, + pub give: OutputValue, +} diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index cc13a38895..147f3558a8 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -14,13 +14,13 @@ // limitations under the License. use common::{ - chain::{output_value::OutputValue, OrderId}, + chain::{output_value::OutputValue, OrderData, OrderId}, primitives::Amount, }; use utils::ensure; use crate::{ - data::{OrderData, OrdersAccountingDeltaData}, + data::OrdersAccountingDeltaData, error::{Error, Result}, operations::{ CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, OrdersAccountingUndo, diff --git a/orders-accounting/src/data.rs b/orders-accounting/src/data.rs index d5cfb8b2f7..b04ad3102d 100644 --- a/orders-accounting/src/data.rs +++ b/orders-accounting/src/data.rs @@ -17,18 +17,11 @@ use std::collections::BTreeMap; use accounting::{DeltaAmountCollection, DeltaDataCollection, DeltaDataUndoCollection}; use common::{ - chain::{output_value::OutputValue, Destination, OrderId}, + chain::{output_value::OutputValue, Destination, OrderData, OrderId}, primitives::Amount, }; use serialization::{Decode, Encode}; -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] -pub struct OrderData { - pub authority: Destination, - pub ask: OutputValue, - pub give: OutputValue, -} - #[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)] pub struct OrdersAccountingData { pub order_data: BTreeMap, diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index f94c7b2f66..6ac06c6dbb 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -21,7 +21,6 @@ mod storage; mod view; pub use { - data::OrderData, error::Error, storage::{db::OrdersAccountingDB, in_memory::InMemoryOrdersAccounting}, view::OrdersAccountingView, diff --git a/orders-accounting/src/operations.rs b/orders-accounting/src/operations.rs index 2fbd5d7be4..d0cbefb47c 100644 --- a/orders-accounting/src/operations.rs +++ b/orders-accounting/src/operations.rs @@ -15,13 +15,13 @@ use accounting::DataDeltaUndo; use common::{ - chain::{output_value::OutputValue, OrderId}, + chain::{output_value::OutputValue, OrderData, OrderId}, primitives::Amount, }; use serialization::{Decode, Encode}; use variant_count::VariantCount; -use crate::{data::OrderData, error::Result}; +use crate::error::Result; #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] pub struct CreateOrderUndo { diff --git a/orders-accounting/src/storage/db.rs b/orders-accounting/src/storage/db.rs index 7dee314b65..7cd46ce895 100644 --- a/orders-accounting/src/storage/db.rs +++ b/orders-accounting/src/storage/db.rs @@ -18,10 +18,13 @@ use std::{collections::BTreeMap, ops::Neg}; use accounting::{ combine_amount_delta, combine_data_with_delta, DeltaAmountCollection, DeltaDataUndoCollection, }; -use common::{chain::OrderId, primitives::Amount}; +use common::{ + chain::{OrderData, OrderId}, + primitives::Amount, +}; use crate::{ - data::{OrderData, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}, + data::{OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}, error::Error, view::{FlushableOrdersAccountingView, OrdersAccountingView}, }; diff --git a/orders-accounting/src/storage/in_memory.rs b/orders-accounting/src/storage/in_memory.rs index 7e427c99cb..ad3a79132f 100644 --- a/orders-accounting/src/storage/in_memory.rs +++ b/orders-accounting/src/storage/in_memory.rs @@ -15,9 +15,12 @@ use std::collections::BTreeMap; -use common::{chain::OrderId, primitives::Amount}; +use common::{ + chain::{OrderData, OrderId}, + primitives::Amount, +}; -use crate::{data::OrderData, view::OrdersAccountingView}; +use crate::view::OrdersAccountingView; use super::{OrdersAccountingStorageRead, OrdersAccountingStorageWrite}; diff --git a/orders-accounting/src/storage/mod.rs b/orders-accounting/src/storage/mod.rs index 16dc2600d5..deb327af18 100644 --- a/orders-accounting/src/storage/mod.rs +++ b/orders-accounting/src/storage/mod.rs @@ -13,11 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use common::{chain::OrderId, primitives::Amount}; +use common::{ + chain::{OrderData, OrderId}, + primitives::Amount, +}; use std::ops::{Deref, DerefMut}; -use crate::data::OrderData; - pub mod db; pub mod in_memory; diff --git a/orders-accounting/src/view.rs b/orders-accounting/src/view.rs index 363557ac3a..20b62ae672 100644 --- a/orders-accounting/src/view.rs +++ b/orders-accounting/src/view.rs @@ -15,9 +15,12 @@ use std::ops::Deref; -use common::{chain::OrderId, primitives::Amount}; +use common::{ + chain::{OrderData, OrderId}, + primitives::Amount, +}; -use crate::data::{OrderData, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}; +use crate::data::{OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}; pub trait OrdersAccountingView { /// Error that can occur during queries From 93287c36318e97831e130b42fca6a457a30cc252 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 30 Apr 2024 10:05:11 +0300 Subject: [PATCH 06/37] FillOrder command initial impl --- .../scanner-lib/src/blockchain_state/mod.rs | 2 + api-server/web-server/src/api/json_helpers.rs | 1 + .../src/constraints_accumulator.rs | 69 +++++++++------ chainstate/src/rpc/types/account.rs | 1 + .../src/signature_destination_getter.rs | 1 + .../src/transaction_verifier/mod.rs | 1 + common/src/chain/tokens/mod.rs | 13 ++- common/src/chain/tokens/nft.rs | 26 +++++- common/src/chain/tokens/tokens_utils.rs | 3 +- .../src/chain/transaction/account_outpoint.rs | 8 +- .../chain/transaction/output/output_value.rs | 13 ++- common/src/primitives/mod.rs | 4 +- mempool/src/pool/entry.rs | 1 + mintscript/src/translate.rs | 5 +- orders-accounting/src/cache.rs | 47 +---------- orders-accounting/src/lib.rs | 2 + orders-accounting/src/price_calculation.rs | 84 +++++++++++++++++++ serialization/src/extras/non_empty_vec.rs | 2 +- wallet/src/account/mod.rs | 3 + wallet/src/account/output_cache/mod.rs | 7 ++ 20 files changed, 213 insertions(+), 80 deletions(-) create mode 100644 orders-accounting/src/price_calculation.rs diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 05918b79df..79b397c60e 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -615,6 +615,7 @@ async fn calculate_fees( | AccountCommand::LockTokenSupply(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) => Some(*token_id), AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, }) .collect(); @@ -1103,6 +1104,7 @@ async fn update_tables_from_transaction_inputs( .await; } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, TxInput::Account(outpoint) => { match outpoint.account() { diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index fef874e798..47c49187bc 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -334,6 +334,7 @@ pub fn tx_input_to_json(inp: &TxInput, chain_config: &ChainConfig) -> serde_json }) } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } } diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 6fc054691a..ebcd79ffc9 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -71,7 +71,7 @@ impl ConstrainedValueAccumulator { ); let mut accumulator = Self::new(); - let mut total_fee_deducted = Amount::ZERO; + let mut total_fee_deducted = BTreeMap::::new(); let mut accounts_balances_tracker = AccountsBalancesTracker::new(pos_accounting_view); for (input, input_utxo) in inputs.iter().zip(inputs_utxos.iter()) { @@ -96,25 +96,26 @@ impl ConstrainedValueAccumulator { )?; } TxInput::AccountCommand(_, command) => { - let fee_to_deduct = accumulator.process_input_account_command( + let (id, fee_to_deduct) = accumulator.process_input_account_command( chain_config, block_height, command, orders_accounting_view, )?; - total_fee_deducted = (total_fee_deducted + fee_to_deduct) - .ok_or(Error::CoinOrTokenOverflow(CoinOrTokenId::Coin))?; + insert_or_increase(&mut total_fee_deducted, id, fee_to_deduct)?; } } } - decrease_or( - &mut accumulator.unconstrained_value, - CoinOrTokenId::Coin, - total_fee_deducted, - Error::AttemptToViolateFeeRequirements, - )?; + for (id, fee) in total_fee_deducted { + decrease_or( + &mut accumulator.unconstrained_value, + id, + fee, + Error::AttemptToViolateFeeRequirements, + )?; + } Ok(accumulator) } @@ -239,7 +240,7 @@ impl ConstrainedValueAccumulator { block_height: BlockHeight, command: &AccountCommand, orders_accounting_view: &impl OrdersAccountingView, - ) -> Result { + ) -> Result<(CoinOrTokenId, Amount), Error> { match command { AccountCommand::MintTokens(token_id, amount) => { insert_or_increase( @@ -247,17 +248,23 @@ impl ConstrainedValueAccumulator { CoinOrTokenId::TokenId(*token_id), *amount, )?; - Ok(chain_config.token_supply_change_fee(block_height)) - } - AccountCommand::LockTokenSupply(_) | AccountCommand::UnmintTokens(_) => { - Ok(chain_config.token_supply_change_fee(block_height)) - } - AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) => { - Ok(chain_config.token_freeze_fee(block_height)) - } - AccountCommand::ChangeTokenAuthority(_, _) => { - Ok(chain_config.token_change_authority_fee(block_height)) + Ok(( + CoinOrTokenId::Coin, + chain_config.token_supply_change_fee(block_height), + )) } + AccountCommand::LockTokenSupply(_) | AccountCommand::UnmintTokens(_) => Ok(( + CoinOrTokenId::Coin, + chain_config.token_supply_change_fee(block_height), + )), + AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) => Ok(( + CoinOrTokenId::Coin, + chain_config.token_freeze_fee(block_height), + )), + AccountCommand::ChangeTokenAuthority(_, _) => Ok(( + CoinOrTokenId::Coin, + chain_config.token_change_authority_fee(block_height), + )), AccountCommand::WithdrawOrder(id) => { let order_data = orders_accounting_view .get_order_data(id) @@ -276,13 +283,27 @@ impl ConstrainedValueAccumulator { let ask_amount = (initially_asked - ask_balance) .ok_or(Error::NegativeAccountBalance(AccountType::Order(*id)))?; - let ask_id = CoinOrTokenId::from_output_value(order_data.ask).expect("cannot fail"); + let ask_id = + CoinOrTokenId::from_output_value(&order_data.ask).expect("cannot fail"); insert_or_increase(&mut self.unconstrained_value, ask_id, ask_amount)?; let give_id = - CoinOrTokenId::from_output_value(order_data.give).expect("cannot fail"); + CoinOrTokenId::from_output_value(&order_data.give).expect("cannot fail"); insert_or_increase(&mut self.unconstrained_value, give_id, give_balance)?; - Ok(Amount::ZERO) + Ok((CoinOrTokenId::Coin, Amount::ZERO)) + } + AccountCommand::FillOrder(order_id, fill_value) => { + let filled_amount = orders_accounting::calculate_fill_order( + &orders_accounting_view, + *order_id, + fill_value, + )?; + + let fill_id = CoinOrTokenId::from_output_value(&fill_value).unwrap(); + insert_or_increase(&mut self.unconstrained_value, fill_id, filled_amount)?; + + let fill_id = CoinOrTokenId::from_output_value(fill_value).unwrap(); + Ok((fill_id, fill_value.amount())) } } } diff --git a/chainstate/src/rpc/types/account.rs b/chainstate/src/rpc/types/account.rs index f86ba1cd49..b93d054e2e 100644 --- a/chainstate/src/rpc/types/account.rs +++ b/chainstate/src/rpc/types/account.rs @@ -104,6 +104,7 @@ impl RpcAccountCommand { } } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }; Ok(result) } diff --git a/chainstate/test-framework/src/signature_destination_getter.rs b/chainstate/test-framework/src/signature_destination_getter.rs index bdf0bb7ce0..356b44ba2c 100644 --- a/chainstate/test-framework/src/signature_destination_getter.rs +++ b/chainstate/test-framework/src/signature_destination_getter.rs @@ -161,6 +161,7 @@ impl<'a> SignatureDestinationGetter<'a> { Ok(destination) } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } }; diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 5c414c010c..9fded621cf 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -584,6 +584,7 @@ where Some(res) } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, }) .collect::, _>>()?; diff --git a/common/src/chain/tokens/mod.rs b/common/src/chain/tokens/mod.rs index b8b64177be..6ed2c549e9 100644 --- a/common/src/chain/tokens/mod.rs +++ b/common/src/chain/tokens/mod.rs @@ -94,7 +94,18 @@ pub struct TokenIssuanceV0 { pub metadata_uri: Vec, } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Encode, + Decode, + serde::Serialize, + serde::Deserialize, +)] pub enum TokenData { /// TokenTransfer data to another user. If it is a token, then the token data must also be transferred to the recipient. #[codec(index = 1)] diff --git a/common/src/chain/tokens/nft.rs b/common/src/chain/tokens/nft.rs index 687a8e67d1..5c96c6444c 100644 --- a/common/src/chain/tokens/nft.rs +++ b/common/src/chain/tokens/nft.rs @@ -22,7 +22,18 @@ pub enum NftIssuance { V0(NftIssuanceV0), } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Encode, + Decode, + serde::Serialize, + serde::Deserialize, +)] pub struct NftIssuanceV0 { pub metadata: Metadata, // TODO: Implement after additional research payout, royalty and refund. @@ -57,7 +68,18 @@ impl From for TokenCreator { } } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, + Clone, + PartialEq, + Eq, + Encode, + PartialOrd, + Ord, + Decode, + serde::Serialize, + serde::Deserialize, +)] pub struct Metadata { pub creator: Option, pub name: Vec, diff --git a/common/src/chain/tokens/tokens_utils.rs b/common/src/chain/tokens/tokens_utils.rs index cd8e8882e9..1db406200f 100644 --- a/common/src/chain/tokens/tokens_utils.rs +++ b/common/src/chain/tokens/tokens_utils.rs @@ -55,7 +55,8 @@ pub fn get_token_supply_change_count(inputs: &[TxInput]) -> usize { AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) - | AccountCommand::WithdrawOrder(_) => false, + | AccountCommand::WithdrawOrder(_) + | AccountCommand::FillOrder(_, _) => false, AccountCommand::MintTokens(_, _) | AccountCommand::UnmintTokens(_) | AccountCommand::LockTokenSupply(_) => true, diff --git a/common/src/chain/transaction/account_outpoint.rs b/common/src/chain/transaction/account_outpoint.rs index 3fe3cefdb1..d7ad284da7 100644 --- a/common/src/chain/transaction/account_outpoint.rs +++ b/common/src/chain/transaction/account_outpoint.rs @@ -22,7 +22,7 @@ use crate::{ }; use serialization::{Decode, Encode}; -use super::Destination; +use super::{output_value::OutputValue, Destination}; /// Type of an account that can be used to identify series of spending from an account #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Encode, Decode)] @@ -53,7 +53,9 @@ impl From for AccountType { | AccountCommand::FreezeToken(id, _) | AccountCommand::UnfreezeToken(id) | AccountCommand::ChangeTokenAuthority(id, _) => AccountType::Token(id), - AccountCommand::WithdrawOrder(id) => AccountType::Order(id), + AccountCommand::WithdrawOrder(id) | AccountCommand::FillOrder(id, _) => { + AccountType::Order(id) + } } } } @@ -115,6 +117,8 @@ pub enum AccountCommand { ChangeTokenAuthority(TokenId, Destination), #[codec(index = 6)] WithdrawOrder(OrderId), + #[codec(index = 7)] + FillOrder(OrderId, OutputValue), } /// Type of OutPoint that represents spending from an account diff --git a/common/src/chain/transaction/output/output_value.rs b/common/src/chain/transaction/output/output_value.rs index 34bd1ce3f2..767b1990d8 100644 --- a/common/src/chain/transaction/output/output_value.rs +++ b/common/src/chain/transaction/output/output_value.rs @@ -20,7 +20,18 @@ use crate::{ primitives::Amount, }; -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, + Clone, + PartialEq, + Eq, + Ord, + PartialOrd, + Encode, + Decode, + serde::Serialize, + serde::Deserialize, +)] pub enum OutputValue { #[codec(index = 0)] Coin(Amount), diff --git a/common/src/primitives/mod.rs b/common/src/primitives/mod.rs index 70f89546b0..9226778544 100644 --- a/common/src/primitives/mod.rs +++ b/common/src/primitives/mod.rs @@ -51,11 +51,11 @@ pub enum CoinOrTokenId { } impl CoinOrTokenId { - pub fn from_output_value(value: OutputValue) -> Option { + pub fn from_output_value(value: &OutputValue) -> Option { match value { OutputValue::Coin(_) => Some(Self::Coin), OutputValue::TokenV0(_) => None, - OutputValue::TokenV1(id, _) => Some(Self::TokenId(id)), + OutputValue::TokenV1(id, _) => Some(Self::TokenId(*id)), } } } diff --git a/mempool/src/pool/entry.rs b/mempool/src/pool/entry.rs index e9d6b98d83..52521cb104 100644 --- a/mempool/src/pool/entry.rs +++ b/mempool/src/pool/entry.rs @@ -75,6 +75,7 @@ impl TxDependency { Self::TokenSupplyAccount(TxAccountDependency::new(acct.clone().into(), nonce)) } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), } } diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index ec87a4408a..3c7b9508ad 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -206,6 +206,7 @@ impl TranslateInput for SignedTransaction { Ok(checksig(dest)) } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } } @@ -295,7 +296,9 @@ impl TranslateInput for TimelockOnly { | AccountCommand::FreezeToken(_token_id, _) | AccountCommand::UnfreezeToken(_token_id) | AccountCommand::ChangeTokenAuthority(_token_id, _) => Ok(WitnessScript::TRUE), - AccountCommand::WithdrawOrder(_) => Ok(WitnessScript::TRUE), + AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _) => { + Ok(WitnessScript::TRUE) + } }, } } diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 147f3558a8..5396df81d1 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -20,6 +20,7 @@ use common::{ use utils::ensure; use crate::{ + calculate_fill_order, data::OrdersAccountingDeltaData, error::{Error, Result}, operations::{ @@ -94,24 +95,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac } fn fill_order(&mut self, id: OrderId, fill_value: OutputValue) -> Result { - let order_data = self.get_order_data(&id)?.ok_or(Error::OrderDataNotFound(id))?; - let give_balance = - self.get_give_balance(&id)?.ok_or(Error::OrderGiveBalanceNotFound(id))?; - let ask_balance = self.get_ask_balance(&id)?.ok_or(Error::OrderAskBalanceNotFound(id))?; - - { - let ask_balance = match order_data.ask { - OutputValue::Coin(_) => OutputValue::Coin(ask_balance), - OutputValue::TokenV0(_) => return Err(Error::UnsupportedTokenVersion), - OutputValue::TokenV1(token_id, _) => OutputValue::TokenV1(token_id, ask_balance), - }; - - // FIXME: given_value > ask_balance should be possible - ensure_currencies_and_amounts_match(id, &ask_balance, &fill_value)?; - } - - let filled_amount = calculate_filled_amount(ask_balance, give_balance, fill_value.amount()) - .ok_or(Error::OrderOverflow(id))?; + let filled_amount = calculate_fill_order(self, id, &fill_value)?; self.data.give_balances.sub_unsigned(id, filled_amount)?; self.data.ask_balances.sub_unsigned(id, fill_value.amount())?; @@ -127,30 +111,3 @@ impl OrdersAccountingOperations for OrdersAccountingCac todo!() } } - -fn calculate_filled_amount(ask: Amount, give: Amount, fill: Amount) -> Option { - (give * fill.into_atoms()).and_then(|v| v / ask.into_atoms()) -} - -fn ensure_currencies_and_amounts_match( - order_id: OrderId, - left: &OutputValue, - right: &OutputValue, -) -> Result<()> { - match (left, right) { - (OutputValue::Coin(amount1), OutputValue::Coin(amount2)) => { - ensure!(amount1 >= amount2, Error::OrderOverflow(order_id)); - Ok(()) - } - (OutputValue::TokenV1(id1, amount1), OutputValue::TokenV1(id2, amount2)) => { - ensure!(amount1 >= amount2, Error::OrderOverflow(order_id)); - ensure!(id1 == id2, Error::CurrencyMismatch); - Ok(()) - } - (OutputValue::Coin(_), OutputValue::TokenV1(_, _)) - | (OutputValue::TokenV1(_, _), OutputValue::Coin(_)) => Err(Error::CurrencyMismatch), - (OutputValue::TokenV0(_), _) | (_, OutputValue::TokenV0(_)) => { - Err(Error::UnsupportedTokenVersion) - } - } -} diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index 6ac06c6dbb..6c595c87a2 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -17,11 +17,13 @@ mod cache; mod data; mod error; mod operations; +mod price_calculation; mod storage; mod view; pub use { error::Error, + price_calculation::calculate_fill_order, storage::{db::OrdersAccountingDB, in_memory::InMemoryOrdersAccounting}, view::OrdersAccountingView, }; diff --git a/orders-accounting/src/price_calculation.rs b/orders-accounting/src/price_calculation.rs new file mode 100644 index 0000000000..7683550a60 --- /dev/null +++ b/orders-accounting/src/price_calculation.rs @@ -0,0 +1,84 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use common::{ + chain::{output_value::OutputValue, OrderId}, + primitives::Amount, +}; +use utils::ensure; + +use crate::{error::Result, Error, OrdersAccountingView}; + +pub fn calculate_fill_order( + view: &impl OrdersAccountingView, + order_id: OrderId, + fill_value: &OutputValue, +) -> Result { + let order_data = view + .get_order_data(&order_id) + .map_err(|_| crate::Error::ViewFail)? + .ok_or(Error::OrderDataNotFound(order_id))?; + let ask_balance = view + .get_ask_balance(&order_id) + .map_err(|_| crate::Error::ViewFail)? + .ok_or(Error::OrderAskBalanceNotFound(order_id))?; + let give_balance = view + .get_give_balance(&order_id) + .map_err(|_| crate::Error::ViewFail)? + .ok_or(Error::OrderGiveBalanceNotFound(order_id))?; + + { + let ask_balance = match order_data.ask { + OutputValue::Coin(_) => OutputValue::Coin(ask_balance), + OutputValue::TokenV0(_) => return Err(Error::UnsupportedTokenVersion), + OutputValue::TokenV1(token_id, _) => OutputValue::TokenV1(token_id, ask_balance), + }; + + // FIXME: fill > ask_balance should be possible + ensure_currencies_and_amounts_match(order_id, &ask_balance, fill_value)?; + } + + calculate_filled_amount_impl(ask_balance, give_balance, fill_value.amount()) + .ok_or(Error::OrderOverflow(order_id)) +} + +fn calculate_filled_amount_impl(ask: Amount, give: Amount, fill: Amount) -> Option { + (give * fill.into_atoms()).and_then(|v| v / ask.into_atoms()) +} + +fn ensure_currencies_and_amounts_match( + order_id: OrderId, + left: &OutputValue, + right: &OutputValue, +) -> Result<()> { + match (left, right) { + (OutputValue::Coin(amount1), OutputValue::Coin(amount2)) => { + ensure!(amount1 >= amount2, Error::OrderOverflow(order_id)); + Ok(()) + } + (OutputValue::TokenV1(id1, amount1), OutputValue::TokenV1(id2, amount2)) => { + ensure!(amount1 >= amount2, Error::OrderOverflow(order_id)); + ensure!(id1 == id2, Error::CurrencyMismatch); + Ok(()) + } + (OutputValue::Coin(_), OutputValue::TokenV1(_, _)) + | (OutputValue::TokenV1(_, _), OutputValue::Coin(_)) => Err(Error::CurrencyMismatch), + (OutputValue::TokenV0(_), _) | (_, OutputValue::TokenV0(_)) => { + Err(Error::UnsupportedTokenVersion) + } + } +} + +// FIXME: tests diff --git a/serialization/src/extras/non_empty_vec.rs b/serialization/src/extras/non_empty_vec.rs index 847040bfde..c053a6f1e3 100644 --- a/serialization/src/extras/non_empty_vec.rs +++ b/serialization/src/extras/non_empty_vec.rs @@ -21,7 +21,7 @@ use serialization_core::{Decode, Encode}; /// - If the Vec has data, it encodes to just the Vec, the Option is omitted /// - If the Vec has no data, it encodes to None /// - Some(vec![]) and None are equivalent when encoded, but when decoded result in None -#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, serde::Serialize, serde::Deserialize)] pub struct DataOrNoVec(Option>); impl DataOrNoVec { diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index cab6c8c068..e6dde6327b 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1346,6 +1346,7 @@ impl Account { .map(|data| (None, Some(data.authority.clone()))) .ok_or(WalletError::UnknownTokenId(*token_id)), AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), } } }) @@ -1824,6 +1825,7 @@ impl Account { || self.is_destination_mine_or_watched(address) } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, }); let relevant_outputs = self.mark_outputs_as_seen(db_tx, tx.outputs())?; @@ -2231,6 +2233,7 @@ fn group_preselected_inputs( )?; } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } } diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index f641358cbc..f46f7125e1 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -671,6 +671,7 @@ impl OutputCache { | AccountCommand::UnfreezeToken(_) => None, AccountCommand::FreezeToken(frozen_token_id, _) => Some(frozen_token_id), AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, }); @@ -729,6 +730,7 @@ impl OutputCache { | AccountCommand::ChangeTokenAuthority(token_id, _) | AccountCommand::UnmintTokens(token_id) => frozen_token_id == token_id, AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, TxInput::Account(_) => false, }) @@ -920,6 +922,7 @@ impl OutputCache { } } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } } @@ -1021,6 +1024,7 @@ impl OutputCache { } } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } } @@ -1310,6 +1314,7 @@ impl OutputCache { } } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } } @@ -1515,6 +1520,7 @@ fn apply_freeze_mutations_from_tx( | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } } @@ -1555,6 +1561,7 @@ fn apply_total_supply_mutations_from_tx( | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, } } From b9951e9ee4b050ef00206e2a12753f1ea9755e3e Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 30 Apr 2024 12:24:41 +0300 Subject: [PATCH 07/37] Add TxOutput::CreateOrder --- .../scanner-lib/src/blockchain_state/mod.rs | 26 ++++++++++++------- .../scanner-lib/src/sync/tests/simulation.rs | 11 +++++--- api-server/web-server/src/api/json_helpers.rs | 1 + .../src/constraints_accumulator.rs | 13 ++++++++-- .../src/detail/chainstateref/epoch_seal.rs | 3 ++- chainstate/src/detail/chainstateref/mod.rs | 6 +++-- chainstate/src/detail/mod.rs | 3 ++- chainstate/src/detail/query.rs | 3 ++- .../interface/chainstate_interface_impl.rs | 1 + chainstate/src/rpc/types/output.rs | 1 + chainstate/test-framework/src/key_manager.rs | 3 ++- .../test-framework/src/random_tx_maker.rs | 7 +++-- .../src/signature_destination_getter.rs | 3 ++- chainstate/test-framework/src/utils.rs | 7 +++-- .../transaction_verifier/check_transaction.rs | 13 +++++++--- .../input_output_policy/mod.rs | 6 +++-- .../input_output_policy/purposes_check.rs | 15 +++++++---- .../tests/outputs_utils.rs | 7 +++-- .../src/transaction_verifier/mod.rs | 12 ++++++--- .../token_issuance_cache.rs | 3 ++- common/src/chain/tokens/tokens_utils.rs | 6 +++-- common/src/chain/transaction/output/mod.rs | 9 +++++-- .../chain/transaction/signature/tests/mod.rs | 1 + .../signature/tests/sign_and_mutate.rs | 1 + common/src/size_estimation/mod.rs | 3 ++- consensus/src/pos/block_sig.rs | 3 ++- consensus/src/pos/mod.rs | 3 ++- mempool/src/pool/tx_pool/store/mem_usage.rs | 1 + mintscript/src/translate.rs | 7 +++-- orders-accounting/src/data.rs | 2 +- orders-accounting/src/lib.rs | 14 ---------- utxo/src/cache.rs | 3 ++- wallet/src/account/currency_grouper/mod.rs | 5 +++- wallet/src/account/mod.rs | 10 ++++--- wallet/src/account/output_cache/mod.rs | 12 ++++++--- wallet/src/account/transaction_list/mod.rs | 3 ++- .../src/account/utxo_selector/output_group.rs | 3 ++- wallet/src/send_request/mod.rs | 3 ++- wallet/types/src/utxo_types.rs | 3 ++- wallet/wallet-controller/src/lib.rs | 3 ++- 40 files changed, 159 insertions(+), 80 deletions(-) diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 79b397c60e..3a9aa162ae 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -396,7 +396,8 @@ async fn update_tables_from_block_reward( | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) - | TxOutput::Htlc(_, _) => {} + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => {} TxOutput::ProduceBlockFromStake(_, _) => { set_utxo( outpoint, @@ -571,7 +572,8 @@ async fn calculate_fees( | TxOutput::DelegateStaking(_, _) | TxOutput::CreateDelegationId(_, _) | TxOutput::ProduceBlockFromStake(_, _) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }) }) .collect(); @@ -604,7 +606,8 @@ async fn calculate_fees( | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::ProduceBlockFromStake(_, _) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }, TxInput::Account(_) => None, TxInput::AccountCommand(_, cmd) => match cmd { @@ -778,7 +781,8 @@ async fn prefetch_pool_data( | TxOutput::DelegateStaking(_, _) | TxOutput::IssueNft(_, _, _) | TxOutput::IssueFungibleToken(_) - | TxOutput::Htlc(_, _), + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_), ) => {} None => {} } @@ -1158,7 +1162,8 @@ async fn update_tables_from_transaction_inputs( | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) - | TxOutput::Htlc(_, _) => {} + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => {} TxOutput::CreateStakePool(pool_id, _) | TxOutput::ProduceBlockFromStake(_, pool_id) => { let pool_data = db_tx @@ -1211,8 +1216,9 @@ async fn update_tables_from_transaction_inputs( | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::IssueFungibleToken(_) => {} - | TxOutput::CreateStakePool(pool_id, _) + | TxOutput::IssueFungibleToken(_) + | TxOutput::CreateOrder(_) => {} + TxOutput::CreateStakePool(pool_id, _) | TxOutput::ProduceBlockFromStake(_, pool_id) => { let pool_data = db_tx .get_pool_data(pool_id) @@ -1514,7 +1520,7 @@ async fn update_tables_from_transaction_outputs( .expect("Unable to encode address"); address_transactions.entry(staker_address).or_default().insert(transaction_id); } - | TxOutput::DelegateStaking(amount, delegation_id) => { + TxOutput::DelegateStaking(amount, delegation_id) => { // Update delegation pledge let delegation = db_tx @@ -1667,6 +1673,7 @@ async fn update_tables_from_transaction_outputs( } } TxOutput::Htlc(_, _) => {} // TODO(HTLC) + TxOutput::CreateOrder(_) => todo!(), } } @@ -1865,7 +1872,8 @@ fn get_tx_output_destination(txo: &TxOutput) -> Option<&Destination> { TxOutput::IssueFungibleToken(_) | TxOutput::Burn(_) | TxOutput::DelegateStaking(_, _) - | TxOutput::DataDeposit(_) => None, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => None, TxOutput::Htlc(_, _) => None, // TODO(HTLC) } } diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index 7d05a22972..d8eae0c70b 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -264,7 +264,8 @@ async fn simulation( | TxOutput::IssueFungibleToken(_) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::IssueNft(_, _, _) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }); staking_pools.extend(new_pools); @@ -283,7 +284,8 @@ async fn simulation( | TxOutput::IssueFungibleToken(_) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::IssueNft(_, _, _) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }); delegations.extend(new_delegations); @@ -299,7 +301,8 @@ async fn simulation( | TxOutput::DelegateStaking(_, _) | TxOutput::CreateDelegationId(_, _) | TxOutput::ProduceBlockFromStake(_, _) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }); token_ids.extend(new_tokens); @@ -344,6 +347,7 @@ async fn simulation( | TxOutput::LockThenTransfer(_, _, _) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::Htlc(_, _) => {} + TxOutput::CreateOrder(_) => todo!(), }); tx.inputs().iter().for_each(|inp| match inp { @@ -389,6 +393,7 @@ async fn simulation( burn_coins(&mut statistics, token_change_authority_fee); } AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::FillOrder(_, _) => todo!(), }, }); } diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index 47c49187bc..90772a4268 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -197,6 +197,7 @@ pub fn txoutput_to_json( }, }) } + TxOutput::CreateOrder(_) => todo!(), } } diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index ebcd79ffc9..7e12c48c45 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -148,8 +148,9 @@ impl ConstrainedValueAccumulator { } TxOutput::CreateDelegationId(..) | TxOutput::IssueFungibleToken(..) - | TxOutput::Burn(_) - | TxOutput::DataDeposit(_) => { + | TxOutput::Burn(..) + | TxOutput::DataDeposit(..) + | TxOutput::CreateOrder(..) => { return Err(Error::SpendingNonSpendableOutput(outpoint.clone())); } TxOutput::IssueNft(token_id, _, _) => { @@ -371,6 +372,14 @@ impl ConstrainedValueAccumulator { CoinOrTokenId::Coin, chain_config.nft_issuance_fee(block_height), )?, + TxOutput::CreateOrder(order_data) => { + let id = CoinOrTokenId::from_output_value(&order_data.give).unwrap(); + insert_or_increase( + &mut accumulator.unconstrained_value, + id, + order_data.give.amount(), + )?; + } }; } diff --git a/chainstate/src/detail/chainstateref/epoch_seal.rs b/chainstate/src/detail/chainstateref/epoch_seal.rs index c1d25c56c8..c8306b4083 100644 --- a/chainstate/src/detail/chainstateref/epoch_seal.rs +++ b/chainstate/src/detail/chainstateref/epoch_seal.rs @@ -175,7 +175,8 @@ where | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => { + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => { return Err(EpochSealError::SpendStakeError( SpendStakeError::InvalidBlockRewardOutputType, )); diff --git a/chainstate/src/detail/chainstateref/mod.rs b/chainstate/src/detail/chainstateref/mod.rs index f0fb4182d4..20770f389d 100644 --- a/chainstate/src/detail/chainstateref/mod.rs +++ b/chainstate/src/detail/chainstateref/mod.rs @@ -698,7 +698,8 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => Err( + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => Err( CheckBlockError::InvalidBlockRewardOutputType(block.get_id()), ), }, @@ -715,7 +716,8 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => Err( + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => Err( CheckBlockError::InvalidBlockRewardOutputType(block.get_id()), ), } diff --git a/chainstate/src/detail/mod.rs b/chainstate/src/detail/mod.rs index 75dad6467f..87d8ef9530 100644 --- a/chainstate/src/detail/mod.rs +++ b/chainstate/src/detail/mod.rs @@ -724,7 +724,8 @@ impl Chainstate | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => { /* do nothing */ } + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => { /* do nothing */ } | TxOutput::CreateStakePool(pool_id, data) => { let _ = db .create_pool(*pool_id, data.as_ref().clone().into()) diff --git a/chainstate/src/detail/query.rs b/chainstate/src/detail/query.rs index 3338a15c20..c9e9307575 100644 --- a/chainstate/src/detail/query.rs +++ b/chainstate/src/detail/query.rs @@ -349,7 +349,8 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, TxOutput::IssueNft(_, issuance, _) => match issuance.as_ref() { NftIssuance::V0(nft) => { Some(RPCTokenInfo::new_nonfungible(RPCNonFungibleTokenInfo::new( diff --git a/chainstate/src/interface/chainstate_interface_impl.rs b/chainstate/src/interface/chainstate_interface_impl.rs index f618300572..c977a950af 100644 --- a/chainstate/src/interface/chainstate_interface_impl.rs +++ b/chainstate/src/interface/chainstate_interface_impl.rs @@ -805,6 +805,7 @@ fn get_output_coin_amount( | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) => None, + TxOutput::CreateOrder(_) => todo!(), }; Ok(amount) diff --git a/chainstate/src/rpc/types/output.rs b/chainstate/src/rpc/types/output.rs index fa0d7f0bed..31cd91ac5b 100644 --- a/chainstate/src/rpc/types/output.rs +++ b/chainstate/src/rpc/types/output.rs @@ -203,6 +203,7 @@ impl RpcTxOutput { TxOutput::DataDeposit(data) => RpcTxOutput::DataDeposit { data: RpcHexString::from_bytes(data), }, + TxOutput::CreateOrder(_) => todo!(), }; Ok(result) } diff --git a/chainstate/test-framework/src/key_manager.rs b/chainstate/test-framework/src/key_manager.rs index 3d449d6ebd..5c2b559d4f 100644 --- a/chainstate/test-framework/src/key_manager.rs +++ b/chainstate/test-framework/src/key_manager.rs @@ -253,7 +253,8 @@ fn is_htlc_output(output: &TxOutput) -> bool { | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) - | TxOutput::DataDeposit(_) => false, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => false, TxOutput::Htlc(_, _) => true, } } diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index 34fb6df4e8..057e72b999 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -280,7 +280,8 @@ impl<'a> RandomTxMaker<'a> { | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => { /* do nothing */ } + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => { /* do nothing */ } TxOutput::CreateStakePool(pool_id, _) => { let (staker_sk, vrf_sk) = new_staking_pools.get(pool_id).unwrap(); staking_pools_observer.on_pool_created( @@ -685,7 +686,8 @@ impl<'a> RandomTxMaker<'a> { | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) - | TxOutput::DataDeposit(_) => unreachable!(), + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => unreachable!(), }; result_inputs.extend(new_inputs); @@ -1048,6 +1050,7 @@ impl<'a> RandomTxMaker<'a> { *dummy_token_id = make_token_id(inputs).unwrap(); Some(output) } + TxOutput::CreateOrder(_) => todo!(), }) .collect(); diff --git a/chainstate/test-framework/src/signature_destination_getter.rs b/chainstate/test-framework/src/signature_destination_getter.rs index 356b44ba2c..414fe63cd3 100644 --- a/chainstate/test-framework/src/signature_destination_getter.rs +++ b/chainstate/test-framework/src/signature_destination_getter.rs @@ -69,7 +69,8 @@ impl<'a> SignatureDestinationGetter<'a> { } TxOutput::CreateDelegationId(_, _) | TxOutput::Burn(_) - | TxOutput::DataDeposit(_) => { + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => { // This error is emitted in other places for attempting to make this spend, // but this is just a double-check. Err(SignatureDestinationGetterError::SigVerifyOfNotSpendableOutput) diff --git a/chainstate/test-framework/src/utils.rs b/chainstate/test-framework/src/utils.rs index bbc85f952d..3164b972f5 100644 --- a/chainstate/test-framework/src/utils.rs +++ b/chainstate/test-framework/src/utils.rs @@ -72,6 +72,7 @@ pub fn get_output_value(output: &TxOutput) -> Option { TxOutput::IssueNft(token_id, _, _) => { Some(OutputValue::TokenV1(*token_id, Amount::from_atoms(1))) } + TxOutput::CreateOrder(_) => todo!(), } } @@ -129,7 +130,8 @@ pub fn create_utxo_data( | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, } } @@ -386,7 +388,8 @@ pub fn find_create_pool_tx_in_genesis(genesis: &Genesis, pool_id: &PoolId) -> Op | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => false, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => false, TxOutput::CreateStakePool(genesis_pool_id, _) => genesis_pool_id == pool_id, }); diff --git a/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs b/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs index bd5e53e0ed..f78971f495 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs @@ -162,7 +162,8 @@ fn check_tokens_tx( | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) - | TxOutput::DataDeposit(_) => false, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => false, }); ensure!( !has_tokens_v0_op, @@ -201,7 +202,8 @@ fn check_tokens_tx( | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => Ok(()), + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => Ok(()), }) .map_err(CheckTransactionError::TokensError)?; @@ -254,7 +256,8 @@ fn check_data_deposit_outputs( | TxOutput::DelegateStaking(..) | TxOutput::IssueFungibleToken(..) | TxOutput::IssueNft(..) - | TxOutput::Htlc(_, _) => { /* Do nothing */ } + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(..) => { /* Do nothing */ } TxOutput::DataDeposit(v) => { // Ensure the size of the data doesn't exceed the max allowed if v.len() > chain_config.data_deposit_max_size() { @@ -271,6 +274,7 @@ fn check_data_deposit_outputs( Ok(()) } +// FIXME: orders here as well fn check_htlc_outputs( chain_config: &ChainConfig, block_height: BlockHeight, @@ -295,7 +299,8 @@ fn check_htlc_outputs( | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) - | TxOutput::DataDeposit(_) => false, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => false, TxOutput::Htlc(_, _) => true, }); diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs index 0e3cca0187..380b2f14b1 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs @@ -75,7 +75,8 @@ pub fn calculate_tokens_burned_in_outputs( | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }) .sum::>() .ok_or(ConnectTransactionError::BurnAmountSumError(tx.get_id())) @@ -229,7 +230,8 @@ fn check_issuance_fee_burn_v0( | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::DelegateStaking(_, _) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }) .sum::>() .ok_or_else(|| ConnectTransactionError::BurnAmountSumError(tx.get_id()))?; diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs index f60b5e9f56..09bb235b70 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs @@ -64,7 +64,8 @@ pub fn check_reward_inputs_outputs_purposes( | TxOutput::IssueFungibleToken(..) | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) - | TxOutput::Htlc(..) => Err(ConnectTransactionError::IOPolicyError( + | TxOutput::Htlc(..) + | TxOutput::CreateOrder(..) => Err(ConnectTransactionError::IOPolicyError( IOPolicyError::InvalidInputTypeInReward, block_id.into(), )), @@ -88,7 +89,8 @@ pub fn check_reward_inputs_outputs_purposes( | TxOutput::IssueFungibleToken(..) | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) - | TxOutput::Htlc(..) => { + | TxOutput::Htlc(..) + | TxOutput::CreateOrder(..) => { Err(ConnectTransactionError::IOPolicyError( IOPolicyError::InvalidOutputTypeInReward, block_id.into(), @@ -139,7 +141,8 @@ pub fn check_reward_inputs_outputs_purposes( | TxOutput::IssueFungibleToken(..) | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) - | TxOutput::Htlc(..) => false, + | TxOutput::Htlc(..) + | TxOutput::CreateOrder(..) => false, }); ensure!( all_lock_then_transfer, @@ -170,7 +173,8 @@ pub fn check_tx_inputs_outputs_purposes( | TxOutput::CreateDelegationId(..) | TxOutput::DelegateStaking(..) | TxOutput::IssueFungibleToken(..) - | TxOutput::DataDeposit(..) => false, + | TxOutput::DataDeposit(..) + | TxOutput::CreateOrder(..) => false, }); ensure!(are_inputs_valid, IOPolicyError::InvalidInputTypeInTx); @@ -203,7 +207,8 @@ pub fn check_tx_inputs_outputs_purposes( | TxOutput::IssueFungibleToken(..) | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) - | TxOutput::Htlc(..) => { /* do nothing */ } + | TxOutput::Htlc(..) + | TxOutput::CreateOrder(..) => { /* do nothing */ } TxOutput::CreateStakePool(..) => { stake_pool_outputs_count += 1; } diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs index 593415e41f..9e4c93d8a5 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs @@ -47,6 +47,7 @@ fn update_functions_below_if_new_outputs_were_added(output: TxOutput) { TxOutput::IssueNft(_, _, _) => unimplemented!(), TxOutput::DataDeposit(_) => unimplemented!(), TxOutput::Htlc(_, _) => unimplemented!(), + TxOutput::CreateOrder(_) => todo!(), } } @@ -244,7 +245,8 @@ pub fn is_stake_pool(output: &TxOutput) -> bool { | TxOutput::IssueFungibleToken(..) | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) - | TxOutput::Htlc(..) => false, + | TxOutput::Htlc(..) + | TxOutput::CreateOrder(..) => false, TxOutput::CreateStakePool(..) => true, } } @@ -260,7 +262,8 @@ pub fn is_produce_block(output: &TxOutput) -> bool { | TxOutput::IssueFungibleToken(..) | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) - | TxOutput::Htlc(..) => false, + | TxOutput::Htlc(..) + | TxOutput::CreateOrder(..) => false, TxOutput::ProduceBlockFromStake(..) => true, } } diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 9fded621cf..16cea43531 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -303,7 +303,8 @@ where | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => Ok(None), + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => Ok(None), } } @@ -423,7 +424,8 @@ where | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }) .collect::, _>>()?; @@ -602,7 +604,8 @@ where | TxOutput::LockThenTransfer(_, _, _) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, TxOutput::IssueFungibleToken(issuance_data) => { let result = make_token_id(tx.inputs()) .ok_or(ConnectTransactionError::TokensError( @@ -674,7 +677,8 @@ where | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => Ok(()), + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => Ok(()), } }) } diff --git a/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs b/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs index 53fd6a1eb6..8a4fa239df 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs @@ -240,7 +240,8 @@ fn has_tokens_issuance_to_cache(outputs: &[TxOutput]) -> Option { | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, TxOutput::IssueNft(id, _, _) => Some(*id), }) } diff --git a/common/src/chain/tokens/tokens_utils.rs b/common/src/chain/tokens/tokens_utils.rs index 1db406200f..58f62cf7a5 100644 --- a/common/src/chain/tokens/tokens_utils.rs +++ b/common/src/chain/tokens/tokens_utils.rs @@ -40,7 +40,8 @@ pub fn get_issuance_count_via_tokens_op(outputs: &[TxOutput]) -> usize { | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => false, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => false, TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) => true, }) .count() @@ -81,7 +82,8 @@ pub fn is_token_or_nft_issuance(output: &TxOutput) -> bool { | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => false, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => false, TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) => true, } } diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs index c075e2c6a6..00bf3da096 100644 --- a/common/src/chain/transaction/output/mod.rs +++ b/common/src/chain/transaction/output/mod.rs @@ -22,9 +22,10 @@ use crate::{ AddressError, }, chain::{ + order::OrderData, output_value::OutputValue, tokens::{IsTokenFreezable, NftIssuance, TokenId, TokenIssuance, TokenTotalSupply}, - ChainConfig, DelegationId, PoolId, + ChainConfig, DelegationId, OrderId, PoolId, }, primitives::{Amount, Id}, text_summary::TextSummary, @@ -140,6 +141,8 @@ pub enum TxOutput { /// Transfer an output under Hashed TimeLock Contract. #[codec(index = 10)] Htlc(OutputValue, Box), + #[codec(index = 11)] + CreateOrder(OrderData), } impl TxOutput { @@ -154,7 +157,8 @@ impl TxOutput { | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, TxOutput::LockThenTransfer(_, _, tl) => Some(tl), } } @@ -326,6 +330,7 @@ impl TextSummary for TxOutput { fmt_dest(&htlc.refund_key) ) } + TxOutput::CreateOrder(_) => todo!(), } } } diff --git a/common/src/chain/transaction/signature/tests/mod.rs b/common/src/chain/transaction/signature/tests/mod.rs index efdc1908eb..bfc108c496 100644 --- a/common/src/chain/transaction/signature/tests/mod.rs +++ b/common/src/chain/transaction/signature/tests/mod.rs @@ -768,6 +768,7 @@ fn check_mutate_output( TxOutput::IssueNft(_, _, _) => unreachable!(), TxOutput::DataDeposit(_) => unreachable!(), TxOutput::Htlc(_, _) => unreachable!(), + TxOutput::CreateOrder(_) => unreachable!(), }; let tx = tx_updater.generate_tx().unwrap(); diff --git a/common/src/chain/transaction/signature/tests/sign_and_mutate.rs b/common/src/chain/transaction/signature/tests/sign_and_mutate.rs index 86c26282f5..03ddc9cdb8 100644 --- a/common/src/chain/transaction/signature/tests/sign_and_mutate.rs +++ b/common/src/chain/transaction/signature/tests/sign_and_mutate.rs @@ -1138,6 +1138,7 @@ fn mutate_output(_rng: &mut impl Rng, tx: &SignedTransactionWithUtxo) -> SignedT TxOutput::IssueNft(_, _, _) => unreachable!(), // TODO: come back to this later TxOutput::DataDeposit(_) => unreachable!(), TxOutput::Htlc(_, _) => unreachable!(), + TxOutput::CreateOrder(_) => unreachable!(), }; SignedTransactionWithUtxo { tx: updater.generate_tx().unwrap(), diff --git a/common/src/size_estimation/mod.rs b/common/src/size_estimation/mod.rs index 189d877161..7d68400dc1 100644 --- a/common/src/size_estimation/mod.rs +++ b/common/src/size_estimation/mod.rs @@ -109,6 +109,7 @@ fn get_tx_output_destination(txo: &TxOutput) -> Option<&Destination> { TxOutput::IssueFungibleToken(_) | TxOutput::Burn(_) | TxOutput::DelegateStaking(_, _) - | TxOutput::DataDeposit(_) => None, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => None, } } diff --git a/consensus/src/pos/block_sig.rs b/consensus/src/pos/block_sig.rs index da359baf39..7138707b2d 100644 --- a/consensus/src/pos/block_sig.rs +++ b/consensus/src/pos/block_sig.rs @@ -51,7 +51,8 @@ fn get_staking_kernel_destination( | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => { + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => { return Err(BlockSignatureError::WrongOutputType(header.get_id())) } TxOutput::CreateStakePool(_, stake_pool) => stake_pool.staker(), diff --git a/consensus/src/pos/mod.rs b/consensus/src/pos/mod.rs index 97988adcf3..fc3216b8c0 100644 --- a/consensus/src/pos/mod.rs +++ b/consensus/src/pos/mod.rs @@ -165,7 +165,8 @@ where | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => { + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => { // only pool outputs can be staked return Err(ConsensusPoSError::RandomnessError( PoSRandomnessError::InvalidOutputTypeInStakeKernel(header.get_id()), diff --git a/mempool/src/pool/tx_pool/store/mem_usage.rs b/mempool/src/pool/tx_pool/store/mem_usage.rs index 0010c702e7..2e5bf615f5 100644 --- a/mempool/src/pool/tx_pool/store/mem_usage.rs +++ b/mempool/src/pool/tx_pool/store/mem_usage.rs @@ -348,6 +348,7 @@ impl MemoryUsage for TxOutput { TxOutput::IssueNft(_, issuance, _) => issuance.indirect_memory_usage(), TxOutput::DataDeposit(v) => v.indirect_memory_usage(), TxOutput::Htlc(_, htlc) => htlc.indirect_memory_usage(), + TxOutput::CreateOrder(_) => 0, } } } diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index 3c7b9508ad..ec4ca5bd7f 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -180,6 +180,7 @@ impl TranslateInput for SignedTransaction { TxOutput::IssueFungibleToken(_issuance) => Err(TranslationError::Unspendable), TxOutput::Burn(_val) => Err(TranslationError::Unspendable), TxOutput::DataDeposit(_data) => Err(TranslationError::Unspendable), + TxOutput::CreateOrder(_) => Err(TranslationError::Unspendable), }, InputInfo::Account { outpoint } => match outpoint.account() { AccountSpending::DelegationBalance(delegation_id, _amount) => { @@ -222,7 +223,8 @@ impl TranslateInput for BlockRewardTransactable<'_> TxOutput::Transfer(_, _) | TxOutput::LockThenTransfer(_, _, _) | TxOutput::IssueNft(_, _, _) - | TxOutput::Htlc(_, _) => Err(TranslationError::IllegalOutputSpend), + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => Err(TranslationError::IllegalOutputSpend), TxOutput::CreateDelegationId(_, _) | TxOutput::Burn(_) | TxOutput::DataDeposit(_) @@ -279,7 +281,8 @@ impl TranslateInput for TimelockOnly { TxOutput::Transfer(_, _) | TxOutput::CreateStakePool(_, _) | TxOutput::ProduceBlockFromStake(_, _) - | TxOutput::IssueNft(_, _, _) => Ok(WitnessScript::TRUE), + | TxOutput::IssueNft(_, _, _) + | TxOutput::CreateOrder(_) => Ok(WitnessScript::TRUE), TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DelegateStaking(_, _) diff --git a/orders-accounting/src/data.rs b/orders-accounting/src/data.rs index b04ad3102d..0b6765273b 100644 --- a/orders-accounting/src/data.rs +++ b/orders-accounting/src/data.rs @@ -17,7 +17,7 @@ use std::collections::BTreeMap; use accounting::{DeltaAmountCollection, DeltaDataCollection, DeltaDataUndoCollection}; use common::{ - chain::{output_value::OutputValue, Destination, OrderData, OrderId}, + chain::{OrderData, OrderId}, primitives::Amount, }; use serialization::{Decode, Encode}; diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index 6c595c87a2..f2bd895da8 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -27,20 +27,6 @@ pub use { storage::{db::OrdersAccountingDB, in_memory::InMemoryOrdersAccounting}, view::OrdersAccountingView, }; -//pub use { -// cache::TokensAccountingCache, -// data::{ -// FungibleTokenData, TokenData, TokensAccountingData, TokensAccountingDeltaData, -// TokensAccountingDeltaUndoData, -// }, -// error::Error, -// operations::{random_undo_for_test, TokenAccountingUndo, TokensAccountingOperations}, -// storage::{ -// db::TokensAccountingDB, in_memory::InMemoryTokensAccounting, TokensAccountingStorageRead, -// TokensAccountingStorageWrite, -// }, -// view::{FlushableTokensAccountingView, TokensAccountingView}, -//}; //#[cfg(test)] //mod tests; diff --git a/utxo/src/cache.rs b/utxo/src/cache.rs index b2d83df015..4e64b30fbc 100644 --- a/utxo/src/cache.rs +++ b/utxo/src/cache.rs @@ -504,7 +504,8 @@ fn should_include_in_utxo_set(output: &TxOutput) -> bool { | TxOutput::DelegateStaking(..) | TxOutput::Burn(..) | TxOutput::IssueFungibleToken(..) - | TxOutput::DataDeposit(..) => false, + | TxOutput::DataDeposit(..) + | TxOutput::CreateOrder(..) => false, } } diff --git a/wallet/src/account/currency_grouper/mod.rs b/wallet/src/account/currency_grouper/mod.rs index 236ed2897f..fbeaf6f663 100644 --- a/wallet/src/account/currency_grouper/mod.rs +++ b/wallet/src/account/currency_grouper/mod.rs @@ -58,6 +58,7 @@ pub(crate) fn group_outputs( get_tx_output(&output).clone(), ))) } + TxOutput::CreateOrder(_) => todo!(), }; match output_value { @@ -112,6 +113,7 @@ pub fn group_outputs_with_issuance_fee( get_tx_output(&output).clone(), ))) } + TxOutput::CreateOrder(_) => todo!(), }; match output_value { @@ -155,7 +157,8 @@ fn output_spendable_value(output: &TxOutput) -> Result<(Currency, Amount), UtxoS | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) - | TxOutput::DataDeposit(_) => { + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => { return Err(UtxoSelectorError::UnsupportedTransactionOutput(Box::new( output.clone(), ))) diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index e6dde6327b..3db7f7897f 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1018,7 +1018,8 @@ impl Account { | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }) .expect("find output with dummy_pool_id"); *old_pool_id = new_pool_id; @@ -1073,7 +1074,8 @@ impl Account { | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, TxOutput::IssueNft(token_id, _, _) => { (*token_id == dummy_token_id).then_some(token_id) } @@ -1547,7 +1549,8 @@ impl Account { TxOutput::IssueFungibleToken(_) | TxOutput::Burn(_) | TxOutput::DelegateStaking(_, _) - | TxOutput::DataDeposit(_) => Vec::new(), + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => Vec::new(), } } @@ -2189,6 +2192,7 @@ fn group_preselected_inputs( output.clone(), ))) } + TxOutput::CreateOrder(_) => todo!(), }; update_preselected_inputs(currency, value, *fee)?; } diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index f46f7125e1..0c38a60385 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -558,7 +558,8 @@ impl OutputCache { | TxOutput::LockThenTransfer(_, _, _) | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) - | TxOutput::Htlc(_, _) => false, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => false, } } @@ -720,6 +721,7 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::ProduceBlockFromStake(_, _) => false, + TxOutput::CreateOrder(_) => todo!(), } }), TxInput::AccountCommand(_, cmd) => match cmd { @@ -833,6 +835,7 @@ impl OutputCache { } } TxOutput::IssueNft(_, _, _) => {} + TxOutput::CreateOrder(_) => todo!(), }; } Ok(()) @@ -1050,6 +1053,7 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::Htlc(_, _) => {} + TxOutput::CreateOrder(_) => todo!(), } } } @@ -1374,7 +1378,8 @@ impl OutputCache { | TxOutput::Burn(_) | TxOutput::Transfer(_, _) | TxOutput::LockThenTransfer(_, _, _) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, TxOutput::ProduceBlockFromStake(_, pool_id) | TxOutput::CreateStakePool(pool_id, _) => { self.pools.get(pool_id).and_then(|pool_data| { @@ -1417,7 +1422,8 @@ fn is_v0_token_output(output: &TxOutput) -> bool { | TxOutput::IssueNft(_, _, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) - | TxOutput::ProduceBlockFromStake(_, _) => false, + | TxOutput::ProduceBlockFromStake(_, _) + | TxOutput::CreateOrder(_) => false, } } diff --git a/wallet/src/account/transaction_list/mod.rs b/wallet/src/account/transaction_list/mod.rs index 5cf323367d..b2b277ea3e 100644 --- a/wallet/src/account/transaction_list/mod.rs +++ b/wallet/src/account/transaction_list/mod.rs @@ -121,7 +121,8 @@ fn own_output(key_chain: &AccountKeyChainImpl, output: &TxOutput) -> bool { | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) - | TxOutput::DataDeposit(_) => false, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => false, } } diff --git a/wallet/src/account/utxo_selector/output_group.rs b/wallet/src/account/utxo_selector/output_group.rs index 017fd545ff..602969e74b 100644 --- a/wallet/src/account/utxo_selector/output_group.rs +++ b/wallet/src/account/utxo_selector/output_group.rs @@ -67,7 +67,8 @@ impl OutputGroup { | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) - | TxOutput::DataDeposit(_) => { + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => { return Err(UtxoSelectorError::UnsupportedTransactionOutput(Box::new( output.1.clone(), ))) diff --git a/wallet/src/send_request/mod.rs b/wallet/src/send_request/mod.rs index ab727178db..da591d7a75 100644 --- a/wallet/src/send_request/mod.rs +++ b/wallet/src/send_request/mod.rs @@ -306,7 +306,8 @@ where TxOutput::IssueFungibleToken(_) | TxOutput::Burn(_) | TxOutput::DelegateStaking(_, _) - | TxOutput::DataDeposit(_) => None, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => None, TxOutput::Htlc(_, _) => None, // TODO(HTLC) } } diff --git a/wallet/types/src/utxo_types.rs b/wallet/types/src/utxo_types.rs index 64555fe2ad..48fe48aa76 100644 --- a/wallet/types/src/utxo_types.rs +++ b/wallet/types/src/utxo_types.rs @@ -52,7 +52,8 @@ pub fn get_utxo_type(output: &TxOutput) -> Option { | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) - | TxOutput::DataDeposit(_) => None, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => None, TxOutput::Htlc(_, _) => None, // TODO(HTLC) } } diff --git a/wallet/wallet-controller/src/lib.rs b/wallet/wallet-controller/src/lib.rs index 91e1205399..4e123df651 100644 --- a/wallet/wallet-controller/src/lib.rs +++ b/wallet/wallet-controller/src/lib.rs @@ -626,7 +626,8 @@ impl Controll | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::Htlc(_, _) => None, + | TxOutput::Htlc(_, _) + | TxOutput::CreateOrder(_) => None, }); let mut balances = BTreeMap::new(); for pool_id in pool_ids { From 121b3f6ee70514e55769512a33dc1fa242b13392 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 30 Apr 2024 21:07:52 +0300 Subject: [PATCH 08/37] Complete order operations --- .../src/constraints_accumulator.rs | 10 +- .../transaction_verifier/check_transaction.rs | 14 +- common/src/chain/order.rs | 28 +++- common/src/chain/tokens/token_id.rs | 4 +- .../chain/transaction/output/output_value.rs | 2 +- orders-accounting/src/cache.rs | 151 +++++++++++++++++- orders-accounting/src/error.rs | 26 ++- orders-accounting/src/lib.rs | 5 +- orders-accounting/src/operations.rs | 17 +- orders-accounting/src/price_calculation.rs | 125 ++++++++++++++- orders-accounting/src/storage/db.rs | 97 ++++++++++- orders-accounting/src/storage/in_memory.rs | 18 --- orders-accounting/src/tests/mod.rs | 16 ++ orders-accounting/src/tests/operations.rs | 139 ++++++++++++++++ 14 files changed, 594 insertions(+), 58 deletions(-) create mode 100644 orders-accounting/src/tests/mod.rs create mode 100644 orders-accounting/src/tests/operations.rs diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 7e12c48c45..05ab5d6186 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -280,16 +280,16 @@ impl ConstrainedValueAccumulator { .map_err(|_| orders_accounting::Error::ViewFail)? .ok_or(orders_accounting::Error::OrderGiveBalanceNotFound(*id))?; - let initially_asked = order_data.ask.amount(); + let initially_asked = order_data.ask().amount(); let ask_amount = (initially_asked - ask_balance) .ok_or(Error::NegativeAccountBalance(AccountType::Order(*id)))?; let ask_id = - CoinOrTokenId::from_output_value(&order_data.ask).expect("cannot fail"); + CoinOrTokenId::from_output_value(order_data.ask()).expect("cannot fail"); insert_or_increase(&mut self.unconstrained_value, ask_id, ask_amount)?; let give_id = - CoinOrTokenId::from_output_value(&order_data.give).expect("cannot fail"); + CoinOrTokenId::from_output_value(order_data.give()).expect("cannot fail"); insert_or_increase(&mut self.unconstrained_value, give_id, give_balance)?; Ok((CoinOrTokenId::Coin, Amount::ZERO)) } @@ -373,11 +373,11 @@ impl ConstrainedValueAccumulator { chain_config.nft_issuance_fee(block_height), )?, TxOutput::CreateOrder(order_data) => { - let id = CoinOrTokenId::from_output_value(&order_data.give).unwrap(); + let id = CoinOrTokenId::from_output_value(order_data.give()).unwrap(); insert_or_increase( &mut accumulator.unconstrained_value, id, - order_data.give.amount(), + order_data.give().amount(), )?; } }; diff --git a/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs b/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs index f78971f495..73fefd444d 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs @@ -156,14 +156,24 @@ fn check_tokens_tx( OutputValue::Coin(_) | OutputValue::TokenV1(_, _) => false, OutputValue::TokenV0(_) => true, }, + TxOutput::CreateOrder(data) => { + let ask_token_v0 = match data.ask() { + OutputValue::Coin(_) | OutputValue::TokenV1(_, _) => false, + OutputValue::TokenV0(_) => true, + }; + let give_token_v0 = match data.ask() { + OutputValue::Coin(_) | OutputValue::TokenV1(_, _) => false, + OutputValue::TokenV0(_) => true, + }; + ask_token_v0 || give_token_v0 + } TxOutput::CreateStakePool(_, _) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) - | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => false, + | TxOutput::DataDeposit(_) => false, }); ensure!( !has_tokens_v0_op, diff --git a/common/src/chain/order.rs b/common/src/chain/order.rs index 772b80032a..720b940d27 100644 --- a/common/src/chain/order.rs +++ b/common/src/chain/order.rs @@ -77,7 +77,29 @@ impl<'de> serde::Deserialize<'de> for OrderId { #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] pub struct OrderData { - pub authority: Destination, - pub ask: OutputValue, - pub give: OutputValue, + authority: Destination, + ask: OutputValue, + give: OutputValue, +} + +impl OrderData { + pub fn new(authority: Destination, ask: OutputValue, give: OutputValue) -> Self { + Self { + authority, + ask, + give, + } + } + + pub fn authority(&self) -> &Destination { + &self.authority + } + + pub fn ask(&self) -> &OutputValue { + &self.ask + } + + pub fn give(&self) -> &OutputValue { + &self.give + } } diff --git a/common/src/chain/tokens/token_id.rs b/common/src/chain/tokens/token_id.rs index c070625a3b..939a079927 100644 --- a/common/src/chain/tokens/token_id.rs +++ b/common/src/chain/tokens/token_id.rs @@ -18,7 +18,7 @@ use crate::{ chain::ChainConfig, primitives::{Id, H256}, }; -use randomness::{CryptoRng, Rng}; +use randomness::Rng; use serialization::{DecodeAll, Encode}; use typename::TypeName; @@ -27,7 +27,7 @@ pub enum Token {} pub type TokenId = Id; impl TokenId { - pub fn random_using(rng: &mut R) -> Self { + pub fn random_using(rng: &mut R) -> Self { Self::new(H256::random_using(rng)) } diff --git a/common/src/chain/transaction/output/output_value.rs b/common/src/chain/transaction/output/output_value.rs index 767b1990d8..be1b6fb562 100644 --- a/common/src/chain/transaction/output/output_value.rs +++ b/common/src/chain/transaction/output/output_value.rs @@ -52,7 +52,7 @@ impl OutputValue { pub fn amount(&self) -> Amount { match self { OutputValue::Coin(v) | OutputValue::TokenV1(_, v) => *v, - OutputValue::TokenV0(_) => Amount::ZERO, // FIXME: convenient but suspicious + OutputValue::TokenV0(_) => unreachable!(), } } } diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 5396df81d1..140293e8d0 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use accounting::combine_amount_delta; use common::{ chain::{output_value::OutputValue, OrderData, OrderId}, primitives::Amount, @@ -25,6 +26,7 @@ use crate::{ error::{Error, Result}, operations::{ CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, OrdersAccountingUndo, + WithdrawOrderUndo, }, view::OrdersAccountingView, }; @@ -49,21 +51,98 @@ impl OrdersAccountingCache

{ pub fn data(&self) -> &OrdersAccountingDeltaData { &self.data } + + fn undo_create_order(&mut self, undo: CreateOrderUndo) -> Result<()> { + ensure!( + self.get_order_data(&undo.id)?.is_some(), + Error::InvariantOrderDataNotFoundForUndo(undo.id) + ); + self.data.order_data.undo_merge_delta_data_element(undo.id, undo.undo_data)?; + + match self.get_ask_balance(&undo.id)? { + Some(balance) => { + if balance != undo.ask_balance { + return Err(Error::InvariantOrderAskBalanceChangedForUndo(undo.id)); + } + } + None => return Err(Error::InvariantOrderAskBalanceNotFoundForUndo(undo.id)), + } + self.data.ask_balances.sub_unsigned(undo.id, undo.ask_balance)?; + + match self.get_give_balance(&undo.id)? { + Some(balance) => { + if balance != undo.give_balance { + return Err(Error::InvariantOrderGiveBalanceChangedForUndo(undo.id)); + } + } + None => return Err(Error::InvariantOrderGiveBalanceNotFoundForUndo(undo.id)), + } + self.data.give_balances.sub_unsigned(undo.id, undo.give_balance)?; + + Ok(()) + } + + fn undo_withdraw_order(&mut self, undo: WithdrawOrderUndo) -> Result<()> { + ensure!( + self.get_order_data(&undo.id)?.is_none(), + Error::InvariantOrderDataExistForWithdrawUndo(undo.id) + ); + self.data.order_data.undo_merge_delta_data_element(undo.id, undo.undo_data)?; + + ensure!( + self.get_ask_balance(&undo.id)?.unwrap_or(Amount::ZERO) == Amount::ZERO, + Error::InvariantOrderAskBalanceExistForWithdrawUndo(undo.id) + ); + self.data.ask_balances.add_unsigned(undo.id, undo.ask_balance)?; + + ensure!( + self.get_give_balance(&undo.id)?.unwrap_or(Amount::ZERO) == Amount::ZERO, + Error::InvariantOrderGiveBalanceExistForWithdrawUndo(undo.id) + ); + self.data.give_balances.add_unsigned(undo.id, undo.give_balance)?; + + Ok(()) + } + + fn undo_fill_order(&mut self, undo: FillOrderUndo) -> Result<()> { + if let Some(undo_data) = undo.undo_data { + ensure!( + self.get_order_data(&undo.id)?.is_none(), + Error::InvariantOrderDataExistForWithdrawUndo(undo.id) + ); + self.data.order_data.undo_merge_delta_data_element(undo.id, undo_data)?; + } + + self.data.ask_balances.add_unsigned(undo.id, undo.ask_balance)?; + self.data.give_balances.add_unsigned(undo.id, undo.give_balance)?; + + Ok(()) + } } impl OrdersAccountingView for OrdersAccountingCache

{ type Error = Error; fn get_order_data(&self, id: &OrderId) -> Result> { - todo!() + match self.data.order_data.get_data(id) { + accounting::GetDataResult::Present(d) => Ok(Some(d.clone())), + accounting::GetDataResult::Deleted => Ok(None), + accounting::GetDataResult::Missing => { + Ok(self.parent.get_order_data(id).map_err(|_| Error::ViewFail)?) + } + } } fn get_ask_balance(&self, id: &OrderId) -> Result> { - todo!() + let parent_supply = self.parent.get_ask_balance(id).map_err(|_| Error::ViewFail)?; + let local_delta = self.data.ask_balances.data().get(id).cloned(); + combine_amount_delta(&parent_supply, &local_delta).map_err(Error::AccountingError) } fn get_give_balance(&self, id: &OrderId) -> Result> { - todo!() + let parent_supply = self.parent.get_give_balance(id).map_err(|_| Error::ViewFail)?; + let local_delta = self.data.give_balances.data().get(id).cloned(); + combine_amount_delta(&parent_supply, &local_delta).map_err(Error::AccountingError) } } @@ -78,8 +157,9 @@ impl OrdersAccountingOperations for OrdersAccountingCac } // FIXME: ask type != give ? - let ask_value = data.ask.clone(); - let give_value = data.give.clone(); + // FIXME: forbid 0 value? + let ask_value = data.ask().clone(); + let give_value = data.give().clone(); let undo_data = self .data .order_data @@ -91,23 +171,78 @@ impl OrdersAccountingOperations for OrdersAccountingCac Ok(OrdersAccountingUndo::CreateOrder(CreateOrderUndo { id, undo_data, + ask_balance: ask_value.amount(), + give_balance: give_value.amount(), + })) + } + + fn withdraw_order(&mut self, id: OrderId) -> Result { + let order_data = self + .get_order_data(&id)? + .ok_or(Error::AttemptedWithdrawNonexistingOrderData(id))?; + let ask_balance = self + .get_ask_balance(&id)? + .ok_or(Error::AttemptedWithdrawNonexistingAskBalance(id))?; + let give_balance = self + .get_give_balance(&id)? + .ok_or(Error::AttemptedWithdrawNonexistingGiveBalance(id))?; + + let undo_data = self + .data + .order_data + .merge_delta_data_element(id, accounting::DataDelta::new(Some(order_data), None))?; + + self.data.ask_balances.add_unsigned(id, ask_balance)?; + self.data.give_balances.add_unsigned(id, give_balance)?; + + Ok(OrdersAccountingUndo::WithdrawOrder(WithdrawOrderUndo { + id, + undo_data, + ask_balance, + give_balance, })) } fn fill_order(&mut self, id: OrderId, fill_value: OutputValue) -> Result { let filled_amount = calculate_fill_order(self, id, &fill_value)?; + let ask_balance = self.get_ask_balance(&id)?.ok_or(Error::OrderAskBalanceNotFound(id))?; + let give_balance = + self.get_give_balance(&id)?.ok_or(Error::OrderGiveBalanceNotFound(id))?; + + // in case the order is completely filled it can be removed + let undo_data = if fill_value.amount() == ask_balance { + ensure!( + filled_amount == give_balance, + Error::FillOrderChangeLeft(id) + ); + + let order_data = self.get_order_data(&id)?.ok_or(Error::OrderDataNotFound(id))?; + let undo = self + .data + .order_data + .merge_delta_data_element(id, accounting::DataDelta::new(Some(order_data), None))?; + Some(undo) + } else { + None + }; + self.data.give_balances.sub_unsigned(id, filled_amount)?; self.data.ask_balances.sub_unsigned(id, fill_value.amount())?; Ok(OrdersAccountingUndo::FillOrder(FillOrderUndo { id, - sub_ask_value: fill_value.amount(), - sub_give_value: filled_amount, + undo_data, + ask_balance: fill_value.amount(), + give_balance: filled_amount, })) } fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()> { - todo!() + match undo_data { + OrdersAccountingUndo::CreateOrder(undo) => self.undo_create_order(undo), + OrdersAccountingUndo::WithdrawOrder(undo) => self.undo_withdraw_order(undo), + OrdersAccountingUndo::FillOrder(undo) => self.undo_fill_order(undo), + } } } diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index 2244603083..52771643bf 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -29,12 +29,34 @@ pub enum Error { OrderAskBalanceNotFound(OrderId), #[error("Give balance for order {0}` not found")] OrderGiveBalanceNotFound(OrderId), - #[error("Unsupported token version")] - UnsupportedTokenVersion, + #[error("Data for order {0}` not found for undo")] + InvariantOrderDataNotFoundForUndo(OrderId), + #[error("Ask balance for order {0}` not found for undo")] + InvariantOrderAskBalanceNotFoundForUndo(OrderId), + #[error("Ask balance for order {0}` changed for undo")] + InvariantOrderAskBalanceChangedForUndo(OrderId), + #[error("Give balance for order {0}` not found for undo")] + InvariantOrderGiveBalanceNotFoundForUndo(OrderId), + #[error("Give balance for order {0}` changed for undo")] + InvariantOrderGiveBalanceChangedForUndo(OrderId), + #[error("Data for order {0}` still exist on withdraw undo")] + InvariantOrderDataExistForWithdrawUndo(OrderId), + #[error("Ask balance for order {0}` still exist on withdraw undo")] + InvariantOrderAskBalanceExistForWithdrawUndo(OrderId), + #[error("Give balance for order {0}` still exist on withdraw undo")] + InvariantOrderGiveBalanceExistForWithdrawUndo(OrderId), + #[error("Fill operation for order {0}` left a change")] + FillOrderChangeLeft(OrderId), #[error("Coin type mismatch")] CurrencyMismatch, #[error("Order overflow: `{0}`")] OrderOverflow(OrderId), + #[error("Attempt to withdraw non-existing order data `{0}`")] + AttemptedWithdrawNonexistingOrderData(OrderId), + #[error("Attempt to withdraw non-existing ask balance `{0}`")] + AttemptedWithdrawNonexistingAskBalance(OrderId), + #[error("Attempt to withdraw non-existing give balance `{0}`")] + AttemptedWithdrawNonexistingGiveBalance(OrderId), // TODO Need a more granular error reporting in the following // https://github.com/mintlayer/mintlayer-core/issues/811 diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index f2bd895da8..5bb4f71c90 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -22,11 +22,12 @@ mod storage; mod view; pub use { + cache::OrdersAccountingCache, error::Error, price_calculation::calculate_fill_order, storage::{db::OrdersAccountingDB, in_memory::InMemoryOrdersAccounting}, view::OrdersAccountingView, }; -//#[cfg(test)] -//mod tests; +#[cfg(test)] +mod tests; diff --git a/orders-accounting/src/operations.rs b/orders-accounting/src/operations.rs index d0cbefb47c..5fc41d124f 100644 --- a/orders-accounting/src/operations.rs +++ b/orders-accounting/src/operations.rs @@ -27,34 +27,37 @@ use crate::error::Result; pub struct CreateOrderUndo { pub(crate) id: OrderId, pub(crate) undo_data: DataDeltaUndo, + pub(crate) ask_balance: Amount, + pub(crate) give_balance: Amount, } #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] -pub struct CancelOrderUndo { +pub struct WithdrawOrderUndo { pub(crate) id: OrderId, pub(crate) undo_data: DataDeltaUndo, - pub(crate) ask_value: OutputValue, - pub(crate) give_value: OutputValue, + pub(crate) ask_balance: Amount, + pub(crate) give_balance: Amount, } #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] pub struct FillOrderUndo { pub(crate) id: OrderId, - pub(crate) sub_ask_value: Amount, - pub(crate) sub_give_value: Amount, + pub(crate) undo_data: Option>, + pub(crate) ask_balance: Amount, + pub(crate) give_balance: Amount, } #[must_use] #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, VariantCount)] pub enum OrdersAccountingUndo { CreateOrder(CreateOrderUndo), - CancelOrder(CancelOrderUndo), + WithdrawOrder(WithdrawOrderUndo), FillOrder(FillOrderUndo), } pub trait OrdersAccountingOperations { fn create_order(&mut self, id: OrderId, data: OrderData) -> Result; - + fn withdraw_order(&mut self, id: OrderId) -> Result; fn fill_order(&mut self, id: OrderId, value: OutputValue) -> Result; fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()>; diff --git a/orders-accounting/src/price_calculation.rs b/orders-accounting/src/price_calculation.rs index 7683550a60..3fa1877d30 100644 --- a/orders-accounting/src/price_calculation.rs +++ b/orders-accounting/src/price_calculation.rs @@ -40,13 +40,12 @@ pub fn calculate_fill_order( .ok_or(Error::OrderGiveBalanceNotFound(order_id))?; { - let ask_balance = match order_data.ask { + let ask_balance = match order_data.ask() { OutputValue::Coin(_) => OutputValue::Coin(ask_balance), - OutputValue::TokenV0(_) => return Err(Error::UnsupportedTokenVersion), - OutputValue::TokenV1(token_id, _) => OutputValue::TokenV1(token_id, ask_balance), + OutputValue::TokenV0(_) => unreachable!(), + OutputValue::TokenV1(token_id, _) => OutputValue::TokenV1(*token_id, ask_balance), }; - // FIXME: fill > ask_balance should be possible ensure_currencies_and_amounts_match(order_id, &ask_balance, fill_value)?; } @@ -76,9 +75,123 @@ fn ensure_currencies_and_amounts_match( (OutputValue::Coin(_), OutputValue::TokenV1(_, _)) | (OutputValue::TokenV1(_, _), OutputValue::Coin(_)) => Err(Error::CurrencyMismatch), (OutputValue::TokenV0(_), _) | (_, OutputValue::TokenV0(_)) => { - Err(Error::UnsupportedTokenVersion) + unreachable!() } } } -// FIXME: tests +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use super::*; + use common::{ + chain::{tokens::TokenId, Destination, OrderData}, + primitives::H256, + }; + use rstest::rstest; + + use crate::{InMemoryOrdersAccounting, OrdersAccountingDB}; + + macro_rules! coin { + ($value:expr) => { + OutputValue::Coin(Amount::from_atoms($value)) + }; + } + + macro_rules! token { + ($value:expr) => { + OutputValue::TokenV1(TokenId::zero(), Amount::from_atoms($value)) + }; + } + + macro_rules! token2 { + ($value:expr) => { + OutputValue::TokenV1(H256::from_low_u64_be(1).into(), Amount::from_atoms($value)) + }; + } + + #[rstest] + #[case(token!(1), coin!(0), token!(0), 0)] + #[case(token!(1), coin!(0), token!(1), 0)] + #[case(token!(3), coin!(100), token!(0), 0)] + #[case(token!(3), coin!(100), token!(1), 33)] + #[case(token!(3), coin!(100), token!(2), 66)] + #[case(token!(3), coin!(100), token!(3), 100)] + #[case(token!(5), coin!(100), token!(0), 0)] + #[case(token!(5), coin!(100), token!(1), 20)] + #[case(token!(5), coin!(100), token!(2), 40)] + #[case(token!(5), coin!(100), token!(3), 60)] + #[case(token!(5), coin!(100), token!(4), 80)] + #[case(token!(5), coin!(100), token!(5), 100)] + #[case(coin!(100), token!(3), coin!(0), 0)] + #[case(coin!(100), token!(3), coin!(1), 0)] + #[case(coin!(100), token!(3), coin!(33), 0)] + #[case(coin!(100), token!(3), coin!(34), 1)] + #[case(coin!(100), token!(3), coin!(66), 1)] + #[case(coin!(100), token!(3), coin!(67), 2)] + #[case(coin!(100), token!(3), coin!(99), 2)] + #[case(coin!(100), token!(3), coin!(100), 3)] + #[case(token!(3), token2!(100), token!(0), 0)] + #[case(token!(3), token2!(100), token!(1), 33)] + #[case(token!(3), token2!(100), token!(2), 66)] + #[case(token!(3), token2!(100), token!(3), 100)] + #[case(coin!(3), coin!(100), coin!(0), 0)] + #[case(coin!(3), coin!(100), coin!(1), 33)] + #[case(coin!(3), coin!(100), coin!(2), 66)] + #[case(coin!(3), coin!(100), coin!(3), 100)] + #[case(coin!(1), token!(u128::MAX), coin!(1), u128::MAX)] + fn valid_values( + #[case] ask: OutputValue, + #[case] give: OutputValue, + #[case] fill: OutputValue, + #[case] result: u128, + ) { + let order_id = OrderId::zero(); + let orders_store = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([( + order_id, + OrderData::new(Destination::AnyoneCanSpend, ask.clone(), give.clone()), + )]), + BTreeMap::from_iter([(order_id, ask.amount())]), + BTreeMap::from_iter([(order_id, give.amount())]), + ); + let orders_db = OrdersAccountingDB::new(&orders_store); + + assert_eq!( + calculate_fill_order(&orders_db, order_id, &fill), + Ok(Amount::from_atoms(result)) + ); + } + + #[rstest] + #[case(token!(0), coin!(1), token!(0), Error::OrderOverflow(OrderId::zero()))] + #[case(token!(0), coin!(1), token!(1), Error::OrderOverflow(OrderId::zero()))] + #[case(coin!(1), token!(1), coin!(2), Error::OrderOverflow(OrderId::zero()))] + #[case(coin!(1), token!(u128::MAX), coin!(2), Error::OrderOverflow(OrderId::zero()))] + #[case(coin!(1), token!(1), token!(1), Error::CurrencyMismatch)] + #[case(coin!(1), token!(1), token!(1), Error::CurrencyMismatch)] + #[case(token!(1), token2!(1), token2!(1), Error::CurrencyMismatch)] + fn invalid_values( + #[case] ask: OutputValue, + #[case] give: OutputValue, + #[case] fill: OutputValue, + #[case] error: Error, + ) { + let order_id = OrderId::zero(); + let orders_store = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([( + order_id, + OrderData::new(Destination::AnyoneCanSpend, ask.clone(), give.clone()), + )]), + BTreeMap::from_iter([(order_id, ask.amount())]), + BTreeMap::from_iter([(order_id, give.amount())]), + ); + let orders_db = OrdersAccountingDB::new(&orders_store); + + assert_eq!( + calculate_fill_order(&orders_db, order_id, &fill), + Err(error) + ); + } +} diff --git a/orders-accounting/src/storage/db.rs b/orders-accounting/src/storage/db.rs index 7cd46ce895..657ac5b88c 100644 --- a/orders-accounting/src/storage/db.rs +++ b/orders-accounting/src/storage/db.rs @@ -22,6 +22,7 @@ use common::{ chain::{OrderData, OrderId}, primitives::Amount, }; +use utils::tap_log::TapLog; use crate::{ data::{OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}, @@ -40,6 +41,99 @@ impl OrdersAccountingDB { } } +impl OrdersAccountingDB { + pub fn merge_with_delta( + &mut self, + other: OrdersAccountingDeltaData, + ) -> Result { + let data_undo = other + .order_data + .consume() + .into_iter() + .map(|(id, delta)| -> Result<_, Error> { + let undo = delta.clone().invert(); + let old_data = self.0.get_order_data(&id).log_err().map_err(|_| Error::ViewFail)?; + match combine_data_with_delta(old_data, Some(delta))? { + Some(result) => self + .0 + .set_order_data(&id, &result) + .log_err() + .map_err(|_| Error::StorageWrite)?, + None => { + self.0.del_order_data(&id).log_err().map_err(|_| Error::StorageWrite)? + } + }; + Ok((id, undo)) + }) + .collect::, _>>()?; + + let ask_balance_undo = other + .ask_balances + .consume() + .into_iter() + .map(|(id, delta)| -> Result<_, Error> { + let balance = self.0.get_ask_balance(&id).log_err().map_err(|_| Error::ViewFail)?; + match combine_amount_delta(&balance, &Some(delta))? { + Some(result) => { + if result > Amount::ZERO { + self.0 + .set_ask_balance(&id, &result) + .log_err() + .map_err(|_| Error::StorageWrite)? + } else { + self.0 + .del_ask_balance(&id) + .log_err() + .map_err(|_| Error::StorageWrite)? + } + } + None => { + self.0.del_ask_balance(&id).log_err().map_err(|_| Error::StorageWrite)? + } + }; + let balance_undo = delta.neg().expect("amount negation some"); + Ok((id, balance_undo)) + }) + .collect::, _>>()?; + + let give_balance_undo = other + .give_balances + .consume() + .into_iter() + .map(|(id, delta)| -> Result<_, Error> { + let balance = + self.0.get_give_balance(&id).log_err().map_err(|_| Error::ViewFail)?; + match combine_amount_delta(&balance, &Some(delta))? { + Some(result) => { + if result > Amount::ZERO { + self.0 + .set_give_balance(&id, &result) + .log_err() + .map_err(|_| Error::StorageWrite)? + } else { + self.0 + .del_give_balance(&id) + .log_err() + .map_err(|_| Error::StorageWrite)? + } + } + None => { + self.0.del_give_balance(&id).log_err().map_err(|_| Error::StorageWrite)? + } + }; + let balance_undo = delta.neg().expect("amount negation some"); + Ok((id, balance_undo)) + }) + .collect::, _>>()?; + + Ok(OrdersAccountingDeltaUndoData { + order_data: DeltaDataUndoCollection::from_data(data_undo), + ask_balances: DeltaAmountCollection::from_iter(ask_balance_undo), + give_balances: DeltaAmountCollection::from_iter(give_balance_undo), + }) + } +} + impl OrdersAccountingView for OrdersAccountingDB { type Error = S::Error; @@ -105,7 +199,6 @@ impl FlushableOrdersAccountingView for OrdersAc &mut self, delta: OrdersAccountingDeltaData, ) -> Result { - //self.merge_with_delta(delta).log_err().map_err(|_| Error::StorageWrite) - todo!() + self.merge_with_delta(delta).log_err().map_err(|_| Error::StorageWrite) } } diff --git a/orders-accounting/src/storage/in_memory.rs b/orders-accounting/src/storage/in_memory.rs index ad3a79132f..fda2a778b7 100644 --- a/orders-accounting/src/storage/in_memory.rs +++ b/orders-accounting/src/storage/in_memory.rs @@ -20,8 +20,6 @@ use common::{ primitives::Amount, }; -use crate::view::OrdersAccountingView; - use super::{OrdersAccountingStorageRead, OrdersAccountingStorageWrite}; #[must_use] @@ -113,19 +111,3 @@ impl OrdersAccountingStorageWrite for InMemoryOrdersAccounting { Ok(()) } } - -impl OrdersAccountingView for InMemoryOrdersAccounting { - type Error = chainstate_types::storage_result::Error; - - fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { - Ok(self.orders_data.get(id).cloned()) - } - - fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { - Ok(self.ask_balances.get(id).cloned()) - } - - fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { - Ok(self.give_balances.get(id).cloned()) - } -} diff --git a/orders-accounting/src/tests/mod.rs b/orders-accounting/src/tests/mod.rs new file mode 100644 index 0000000000..180c12f7f1 --- /dev/null +++ b/orders-accounting/src/tests/mod.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod operations; diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs new file mode 100644 index 0000000000..417ff0b6be --- /dev/null +++ b/orders-accounting/src/tests/operations.rs @@ -0,0 +1,139 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::BTreeMap; + +use common::{ + chain::{output_value::OutputValue, tokens::TokenId, Destination, OrderData, OrderId}, + primitives::Amount, +}; +use randomness::Rng; +use rstest::rstest; +use test_utils::random::{make_seedable_rng, Seed}; + +use crate::{ + cache::OrdersAccountingCache, operations::OrdersAccountingOperations, + view::FlushableOrdersAccountingView, InMemoryOrdersAccounting, OrdersAccountingDB, + OrdersAccountingView, +}; + +fn make_order_data(rng: &mut impl Rng) -> OrderData { + let token_id = TokenId::random_using(rng); + OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(Amount::from_atoms(rng.gen_range(1u128..1000))), + OutputValue::TokenV1(token_id, Amount::from_atoms(rng.gen_range(1u128..1000))), + ) +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_order_and_flush(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let order_data = make_order_data(&mut rng); + + let mut storage = InMemoryOrdersAccounting::new(); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + let _ = cache.create_order(order_id, order_data.clone()).unwrap(); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + let expected_storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + + assert_eq!(expected_storage, storage); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_order_and_undo(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let order_data = make_order_data(&mut rng); + + let mut storage = InMemoryOrdersAccounting::new(); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + let undo = cache.create_order(order_id, order_data.clone()).unwrap(); + + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(order_data.ask().amount()), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(order_data.give().amount()), + cache.get_give_balance(&order_id).unwrap() + ); + + cache.undo(undo).unwrap(); + + assert_eq!(None, cache.get_order_data(&order_id).unwrap().as_ref()); + assert_eq!(None, cache.get_ask_balance(&order_id).unwrap()); + assert_eq!(None, cache.get_give_balance(&order_id).unwrap()); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + assert_eq!(InMemoryOrdersAccounting::new(), storage); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_order_must_converge(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let ask = rng.gen_range(1u128..1000); + let give = rng.gen_range(1u128..1000); + let fill_orders = test_utils::split_value(&mut rng, ask); + + let ask = OutputValue::Coin(Amount::from_atoms(ask)); + let give = OutputValue::Coin(Amount::from_atoms(give)); + + let order_id = OrderId::random_using(&mut rng); + let order_data = OrderData::new(Destination::AnyoneCanSpend, ask.clone(), give.clone()); + let mut storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, ask.amount())]), + BTreeMap::from_iter([(order_id, give.amount())]), + ); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + for fill in fill_orders { + let _ = cache.fill_order(order_id, OutputValue::Coin(Amount::from_atoms(fill))).unwrap(); + } + + db.batch_write_orders_data(cache.consume()).unwrap(); + + let expected_storage = + InMemoryOrdersAccounting::from_values(BTreeMap::new(), BTreeMap::new(), BTreeMap::new()); + + assert_eq!(expected_storage, storage); +} From 2e81d528d7949654533c8eec53e3bf858f7a625b Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Wed, 1 May 2024 13:37:38 +0300 Subject: [PATCH 09/37] Add tests for order constraints --- .../src/constraints_accumulator.rs | 20 +- .../src/tests/mod.rs | 1 + .../src/tests/orders_constraints.rs | 709 ++++++++++++++++++ .../src/chain/transaction/account_outpoint.rs | 2 +- orders-accounting/src/tests/operations.rs | 7 +- 5 files changed, 726 insertions(+), 13 deletions(-) create mode 100644 chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 05ab5d6186..c5b093e1b1 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -284,13 +284,13 @@ impl ConstrainedValueAccumulator { let ask_amount = (initially_asked - ask_balance) .ok_or(Error::NegativeAccountBalance(AccountType::Order(*id)))?; - let ask_id = + let ask_currency = CoinOrTokenId::from_output_value(order_data.ask()).expect("cannot fail"); - insert_or_increase(&mut self.unconstrained_value, ask_id, ask_amount)?; + insert_or_increase(&mut self.unconstrained_value, ask_currency, ask_amount)?; - let give_id = + let give_currency = CoinOrTokenId::from_output_value(order_data.give()).expect("cannot fail"); - insert_or_increase(&mut self.unconstrained_value, give_id, give_balance)?; + insert_or_increase(&mut self.unconstrained_value, give_currency, give_balance)?; Ok((CoinOrTokenId::Coin, Amount::ZERO)) } AccountCommand::FillOrder(order_id, fill_value) => { @@ -300,11 +300,15 @@ impl ConstrainedValueAccumulator { fill_value, )?; - let fill_id = CoinOrTokenId::from_output_value(&fill_value).unwrap(); - insert_or_increase(&mut self.unconstrained_value, fill_id, filled_amount)?; + let order_data = orders_accounting_view + .get_order_data(order_id) + .map_err(|_| orders_accounting::Error::ViewFail)? + .ok_or(orders_accounting::Error::OrderDataNotFound(*order_id))?; + let give_currency = CoinOrTokenId::from_output_value(order_data.give()).unwrap(); + insert_or_increase(&mut self.unconstrained_value, give_currency, filled_amount)?; - let fill_id = CoinOrTokenId::from_output_value(fill_value).unwrap(); - Ok((fill_id, fill_value.amount())) + let ask_currency = CoinOrTokenId::from_output_value(fill_value).unwrap(); + Ok((ask_currency, fill_value.amount())) } } } diff --git a/chainstate/constraints-value-accumulator/src/tests/mod.rs b/chainstate/constraints-value-accumulator/src/tests/mod.rs index be5d5cef5a..5e440afd2b 100644 --- a/chainstate/constraints-value-accumulator/src/tests/mod.rs +++ b/chainstate/constraints-value-accumulator/src/tests/mod.rs @@ -15,3 +15,4 @@ mod constraints_tests; mod homomorphism; +mod orders_constraints; diff --git a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs new file mode 100644 index 0000000000..01176e866b --- /dev/null +++ b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs @@ -0,0 +1,709 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::BTreeMap; + +use common::{ + chain::{ + config::create_unit_test_config, output_value::OutputValue, tokens::TokenId, + AccountCommand, AccountNonce, Destination, OrderData, OrderId, OutPointSourceId, TxInput, + TxOutput, UtxoOutPoint, + }, + primitives::{Amount, BlockHeight, CoinOrTokenId, Fee, Id, H256}, +}; +use orders_accounting::{InMemoryOrdersAccounting, OrdersAccountingDB}; +use pos_accounting::{InMemoryPoSAccounting, PoSAccountingDB}; +use randomness::Rng; +use rstest::rstest; +use test_utils::random::{make_seedable_rng, Seed}; + +use crate::{ConstrainedValueAccumulator, Error}; + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_order_constraints(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let chain_config = create_unit_test_config(); + let block_height = BlockHeight::one(); + + let pos_store = InMemoryPoSAccounting::new(); + let pos_db = PoSAccountingDB::new(&pos_store); + + let give_amount = Amount::from_atoms(rng.gen_range(100..1000)); + let token_id = TokenId::random_using(&mut rng); + let ask_amount = Amount::from_atoms(rng.gen_range(100..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, ask_amount), + OutputValue::Coin(give_amount), + ); + + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + + // not enough input coins + { + let inputs = vec![TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + ))]; + let input_utxos = vec![Some(TxOutput::Transfer( + OutputValue::Coin((give_amount - Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, + ))]; + + let outputs = vec![TxOutput::CreateOrder(order_data.clone())]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let result = inputs_accumulator.satisfy_with(outputs_accumulator); + + assert_eq!( + result.unwrap_err(), + Error::AttemptToPrintMoneyOrViolateTimelockConstraints(CoinOrTokenId::Coin) + ); + } + + // input tokens instead of coins + { + let inputs = vec![TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + ))]; + let input_utxos = vec![Some(TxOutput::Transfer( + OutputValue::TokenV1(token_id, give_amount), + Destination::AnyoneCanSpend, + ))]; + + let outputs = vec![TxOutput::CreateOrder(order_data.clone())]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let result = inputs_accumulator.satisfy_with(outputs_accumulator); + + assert_eq!( + result.unwrap_err(), + Error::AttemptToPrintMoneyOrViolateTimelockConstraints(CoinOrTokenId::Coin) + ); + } + + // print coins in output + { + let inputs = vec![TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + ))]; + let input_utxos = vec![Some(TxOutput::Transfer( + OutputValue::Coin(give_amount), + Destination::AnyoneCanSpend, + ))]; + + let outputs = vec![ + TxOutput::CreateOrder(order_data.clone()), + TxOutput::Transfer( + OutputValue::Coin(Amount::from_atoms(1)), + Destination::AnyoneCanSpend, + ), + ]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let result = inputs_accumulator.satisfy_with(outputs_accumulator); + + assert_eq!( + result.unwrap_err(), + Error::AttemptToPrintMoneyOrViolateTimelockConstraints(CoinOrTokenId::Coin) + ); + } + + // print tokens in output + { + let inputs = vec![TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + ))]; + let input_utxos = vec![Some(TxOutput::Transfer( + OutputValue::Coin(give_amount), + Destination::AnyoneCanSpend, + ))]; + + let outputs = vec![ + TxOutput::CreateOrder(order_data.clone()), + TxOutput::Transfer( + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + Destination::AnyoneCanSpend, + ), + ]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let result = inputs_accumulator.satisfy_with(outputs_accumulator); + + assert_eq!( + result.unwrap_err(), + Error::AttemptToPrintMoneyOrViolateTimelockConstraints(CoinOrTokenId::TokenId( + token_id + )) + ); + } + + // valid case + let inputs = vec![TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + ))]; + let input_utxos = vec![Some(TxOutput::Transfer( + OutputValue::Coin(give_amount), + Destination::AnyoneCanSpend, + ))]; + + let outputs = vec![TxOutput::CreateOrder(order_data)]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs).unwrap(); + + let accumulated_fee = inputs_accumulator + .satisfy_with(outputs_accumulator) + .unwrap() + .map_into_block_fees(&chain_config, block_height) + .unwrap(); + + assert_eq!(accumulated_fee, Fee(Amount::ZERO)); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_order_constraints(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let chain_config = create_unit_test_config(); + let block_height = BlockHeight::one(); + + let pos_store = InMemoryPoSAccounting::new(); + let pos_db = PoSAccountingDB::new(&pos_store); + + let order_id = OrderId::random_using(&mut rng); + let give_amount = Amount::from_atoms(rng.gen_range(100..1000)); + let token_id = TokenId::random_using(&mut rng); + let ask_amount = Amount::from_atoms(rng.gen_range(100..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, ask_amount), + OutputValue::Coin(give_amount), + ); + + let orders_store = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, ask_amount)]), + BTreeMap::from_iter([(order_id, give_amount)]), + ); + let orders_db = OrdersAccountingDB::new(&orders_store); + + // use in command more than provided in input + { + let inputs = vec![ + TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + )), + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder( + order_id, + OutputValue::TokenV1(token_id, (ask_amount + Amount::from_atoms(1)).unwrap()), + ), + ), + ]; + let input_utxos = vec![ + Some(TxOutput::Transfer( + OutputValue::TokenV1(token_id, ask_amount), + Destination::AnyoneCanSpend, + )), + None, + ]; + + let result = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ); + + assert_eq!( + result.unwrap_err(), + Error::OrdersAccountingError(orders_accounting::Error::OrderOverflow(order_id)) + ); + } + + // fill with coins instead of tokens + { + let inputs = vec![ + TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + )), + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder(order_id, OutputValue::Coin(ask_amount)), + ), + ]; + let input_utxos = vec![ + Some(TxOutput::Transfer( + OutputValue::Coin(ask_amount), + Destination::AnyoneCanSpend, + )), + None, + ]; + + let result = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ); + + assert_eq!( + result.unwrap_err(), + Error::OrdersAccountingError(orders_accounting::Error::CurrencyMismatch) + ); + } + + // try to print coins in output + { + let inputs = vec![ + TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + )), + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder(order_id, OutputValue::TokenV1(token_id, ask_amount)), + ), + ]; + let input_utxos = vec![ + Some(TxOutput::Transfer( + OutputValue::TokenV1(token_id, ask_amount), + Destination::AnyoneCanSpend, + )), + None, + ]; + + let outputs = vec![TxOutput::Transfer( + OutputValue::Coin((give_amount + Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, + )]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let result = inputs_accumulator.satisfy_with(outputs_accumulator); + + assert_eq!( + result.unwrap_err(), + Error::AttemptToPrintMoneyOrViolateTimelockConstraints(CoinOrTokenId::Coin) + ); + } + + // try to print tokens in output + { + let inputs = vec![ + TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + )), + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder(order_id, OutputValue::TokenV1(token_id, ask_amount)), + ), + ]; + let input_utxos = vec![ + Some(TxOutput::Transfer( + OutputValue::TokenV1(token_id, ask_amount), + Destination::AnyoneCanSpend, + )), + None, + ]; + + let outputs = vec![ + TxOutput::Transfer(OutputValue::Coin(give_amount), Destination::AnyoneCanSpend), + TxOutput::Transfer( + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + Destination::AnyoneCanSpend, + ), + ]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let result = inputs_accumulator.satisfy_with(outputs_accumulator); + + assert_eq!( + result.unwrap_err(), + Error::AttemptToPrintMoneyOrViolateTimelockConstraints(CoinOrTokenId::TokenId( + token_id + )) + ); + } + + { + // partially use input in command + let inputs = vec![ + TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + )), + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder(order_id, OutputValue::TokenV1(token_id, ask_amount)), + ), + ]; + let input_utxos = vec![ + Some(TxOutput::Transfer( + OutputValue::TokenV1(token_id, (ask_amount + Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, + )), + None, + ]; + + let outputs = vec![ + TxOutput::Transfer(OutputValue::Coin(give_amount), Destination::AnyoneCanSpend), + TxOutput::Transfer( + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + Destination::AnyoneCanSpend, + ), + ]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let accumulated_fee = inputs_accumulator + .satisfy_with(outputs_accumulator) + .unwrap() + .map_into_block_fees(&chain_config, block_height) + .unwrap(); + + assert_eq!(accumulated_fee, Fee(Amount::ZERO)); + } + + // valid case + let inputs = vec![ + TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))), + 0, + )), + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder(order_id, OutputValue::TokenV1(token_id, ask_amount)), + ), + ]; + let input_utxos = vec![ + Some(TxOutput::Transfer( + OutputValue::TokenV1(token_id, ask_amount), + Destination::AnyoneCanSpend, + )), + None, + ]; + + let outputs = + vec![TxOutput::Transfer(OutputValue::Coin(give_amount), Destination::AnyoneCanSpend)]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs).unwrap(); + + let accumulated_fee = inputs_accumulator + .satisfy_with(outputs_accumulator) + .unwrap() + .map_into_block_fees(&chain_config, block_height) + .unwrap(); + + assert_eq!(accumulated_fee, Fee(Amount::ZERO)); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn withdraw_order_constraints(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let chain_config = create_unit_test_config(); + let block_height = BlockHeight::one(); + + let pos_store = InMemoryPoSAccounting::new(); + let pos_db = PoSAccountingDB::new(&pos_store); + + let order_id = OrderId::random_using(&mut rng); + let give_amount = Amount::from_atoms(rng.gen_range(100..1000)); + let token_id = TokenId::random_using(&mut rng); + let ask_amount = Amount::from_atoms(rng.gen_range(100..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, ask_amount), + OutputValue::Coin(give_amount), + ); + + let orders_store = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, ask_amount)]), + BTreeMap::from_iter([(order_id, give_amount)]), + ); + let orders_db = OrdersAccountingDB::new(&orders_store); + + // try to print coins in output + { + let inputs = vec![TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(order_id), + )]; + let input_utxos = vec![None]; + + let outputs = vec![TxOutput::Transfer( + OutputValue::Coin((give_amount + Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, + )]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let result = inputs_accumulator.satisfy_with(outputs_accumulator); + + assert_eq!( + result.unwrap_err(), + Error::AttemptToPrintMoneyOrViolateTimelockConstraints(CoinOrTokenId::Coin) + ); + } + + // try to print tokens in output + { + let inputs = vec![TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(order_id), + )]; + let input_utxos = vec![None]; + + let outputs = vec![TxOutput::Transfer( + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + Destination::AnyoneCanSpend, + )]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let result = inputs_accumulator.satisfy_with(outputs_accumulator); + + assert_eq!( + result.unwrap_err(), + Error::AttemptToPrintMoneyOrViolateTimelockConstraints(CoinOrTokenId::TokenId( + token_id + )) + ); + } + + { + // partially use input in command + let inputs = vec![TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(order_id), + )]; + let input_utxos = vec![None]; + + let outputs = vec![TxOutput::Transfer( + OutputValue::Coin((give_amount - Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, + )]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs) + .unwrap(); + + let accumulated_fee = inputs_accumulator + .satisfy_with(outputs_accumulator) + .unwrap() + .map_into_block_fees(&chain_config, block_height) + .unwrap(); + + assert_eq!(accumulated_fee, Fee(Amount::from_atoms(1))); + } + + // valid case + let inputs = vec![TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(order_id), + )]; + let input_utxos = vec![None]; + + let outputs = + vec![TxOutput::Transfer(OutputValue::Coin(give_amount), Destination::AnyoneCanSpend)]; + + let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( + &chain_config, + block_height, + &orders_db, + &pos_db, + &inputs, + &input_utxos, + ) + .unwrap(); + + let outputs_accumulator = + ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs).unwrap(); + + let accumulated_fee = inputs_accumulator + .satisfy_with(outputs_accumulator) + .unwrap() + .map_into_block_fees(&chain_config, block_height) + .unwrap(); + + assert_eq!(accumulated_fee, Fee(Amount::ZERO)); +} diff --git a/common/src/chain/transaction/account_outpoint.rs b/common/src/chain/transaction/account_outpoint.rs index d7ad284da7..765c955304 100644 --- a/common/src/chain/transaction/account_outpoint.rs +++ b/common/src/chain/transaction/account_outpoint.rs @@ -32,7 +32,7 @@ pub enum AccountType { /// Token account type is used to authorize changes in token data. #[codec(index = 1)] Token(TokenId), - #[codec(index = 1)] + #[codec(index = 2)] Order(OrderId), } diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index 417ff0b6be..f9499185ba 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -132,8 +132,7 @@ fn fill_order_must_converge(#[case] seed: Seed) { db.batch_write_orders_data(cache.consume()).unwrap(); - let expected_storage = - InMemoryOrdersAccounting::from_values(BTreeMap::new(), BTreeMap::new(), BTreeMap::new()); - - assert_eq!(expected_storage, storage); + assert_eq!(InMemoryOrdersAccounting::new(), storage); } + +// FIXME: more tests From b6b361c328260175f3f0845b0cc9432d77858b79 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Wed, 1 May 2024 15:01:10 +0300 Subject: [PATCH 10/37] Initial chainstate integration --- Cargo.lock | 4 + .../detail/chainstateref/in_memory_reorg.rs | 2 + .../chainstateref/tx_verifier_storage.rs | 39 +++++++++- .../default_strategy.rs | 15 ++-- .../detail/tx_verification_strategy/mod.rs | 23 +++--- chainstate/storage/Cargo.toml | 1 + .../src/internal/store_tx/read_impls.rs | 43 ++++++++++- .../src/internal/store_tx/write_impls.rs | 37 ++++++++- chainstate/storage/src/lib.rs | 3 + chainstate/storage/src/mock/mock_impl.rs | 48 +++++++++++- chainstate/test-framework/Cargo.toml | 1 + .../disposable_strategy.rs | 15 ++-- .../randomized_strategy.rs | 39 ++++++---- chainstate/test-suite/Cargo.toml | 1 + .../helpers/in_memory_storage_wrapper.rs | 21 ++++- .../src/tests/tx_verifier_among_threads.rs | 29 ++++++- .../src/transaction_verifier/hierarchy.rs | 76 ++++++++++++------- .../src/transaction_verifier/mod.rs | 32 +++++++- .../src/transaction_verifier/storage.rs | 6 +- .../src/transaction_verifier/tests/mock.rs | 19 ++++- mempool/Cargo.toml | 1 + .../tx_pool/tx_verifier/chainstate_handle.rs | 36 ++++++++- mempool/src/pool/tx_pool/tx_verifier/mod.rs | 2 + orders-accounting/src/cache.rs | 12 +++ orders-accounting/src/data.rs | 23 ++++++ orders-accounting/src/lib.rs | 9 ++- 26 files changed, 453 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1ba59eb1e..1b77c20ade 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1215,6 +1215,7 @@ dependencies = [ "logging", "mockall", "num-traits", + "orders-accounting", "parity-scale-codec", "pos-accounting", "randomness", @@ -1242,6 +1243,7 @@ dependencies = [ "hex", "itertools 0.13.0", "logging", + "orders-accounting", "pos-accounting", "randomness", "rstest", @@ -1275,6 +1277,7 @@ dependencies = [ "hex", "itertools 0.13.0", "logging", + "orders-accounting", "pos-accounting", "randomness", "rstest", @@ -4078,6 +4081,7 @@ dependencies = [ "mintscript", "mockall", "num-traits", + "orders-accounting", "p2p-types", "parking_lot 0.12.3", "pos-accounting", diff --git a/chainstate/src/detail/chainstateref/in_memory_reorg.rs b/chainstate/src/detail/chainstateref/in_memory_reorg.rs index 0fde9add2d..2aa6507e62 100644 --- a/chainstate/src/detail/chainstateref/in_memory_reorg.rs +++ b/chainstate/src/detail/chainstateref/in_memory_reorg.rs @@ -23,6 +23,7 @@ use common::{ chain::{Block, ChainConfig, GenBlock, GenBlockId}, primitives::{id::WithId, Id}, }; +use orders_accounting::OrdersAccountingDB; use thiserror::Error; use tokens_accounting::TokensAccountingDB; use tx_verifier::{ @@ -197,6 +198,7 @@ type TxVerifier<'a, 'b, S, V> = TransactionVerifier< UtxosDB<&'b ChainstateRef<'a, S, V>>, &'b ChainstateRef<'a, S, V>, TokensAccountingDB<&'b ChainstateRef<'a, S, V>>, + OrdersAccountingDB<&'b ChainstateRef<'a, S, V>>, >; #[derive(Error, Debug, PartialEq, Eq, Clone)] diff --git a/chainstate/src/detail/chainstateref/tx_verifier_storage.rs b/chainstate/src/detail/chainstateref/tx_verifier_storage.rs index e560ea7d31..3d34be1d0a 100644 --- a/chainstate/src/detail/chainstateref/tx_verifier_storage.rs +++ b/chainstate/src/detail/chainstateref/tx_verifier_storage.rs @@ -28,11 +28,12 @@ use chainstate_types::{storage_result, GenBlockIndex}; use common::{ chain::{ tokens::{TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, ChainConfig, DelegationId, GenBlock, GenBlockId, PoolId, - Transaction, + AccountNonce, AccountType, ChainConfig, DelegationId, GenBlock, GenBlockId, OrderData, + OrderId, PoolId, Transaction, }, primitives::{Amount, Id}, }; +use orders_accounting::{FlushableOrdersAccountingView, OrdersAccountingStorageRead}; use pos_accounting::{ DelegationData, DeltaMergeUndo, FlushablePoSAccountingView, PoSAccountingDB, PoSAccountingDeltaData, PoSAccountingUndo, PoSAccountingView, PoolData, @@ -494,3 +495,37 @@ impl<'a, S: BlockchainStorageWrite, V: TransactionVerificationStrategy> db.batch_write_tokens_data(delta) } } + +impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> OrdersAccountingStorageRead + for ChainstateRef<'a, S, V> +{ + type Error = storage_result::Error; + + #[log_error] + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + #[log_error] + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + #[log_error] + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } +} + +impl<'a, S: BlockchainStorageWrite, V: TransactionVerificationStrategy> + FlushableOrdersAccountingView for ChainstateRef<'a, S, V> +{ + type Error = orders_accounting::Error; + + fn batch_write_orders_data( + &mut self, + delta: orders_accounting::OrdersAccountingDeltaData, + ) -> Result { + todo!() + } +} diff --git a/chainstate/src/detail/tx_verification_strategy/default_strategy.rs b/chainstate/src/detail/tx_verification_strategy/default_strategy.rs index 08132b554b..3c3da64f65 100644 --- a/chainstate/src/detail/tx_verification_strategy/default_strategy.rs +++ b/chainstate/src/detail/tx_verification_strategy/default_strategy.rs @@ -21,6 +21,7 @@ use common::{ primitives::{id::WithId, Idable}, }; use constraints_value_accumulator::AccumulatedFee; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use tokens_accounting::TokensAccountingView; use tx_verifier::{ @@ -48,7 +49,7 @@ impl Default for DefaultTransactionVerificationStrategy { } impl TransactionVerificationStrategy for DefaultTransactionVerificationStrategy { - fn connect_block( + fn connect_block( &self, tx_verifier_maker: M, storage_backend: S, @@ -56,14 +57,15 @@ impl TransactionVerificationStrategy for DefaultTransactionVerificationStrategy block_index: &BlockIndex, block: &WithId, median_time_past: BlockTimestamp, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef + ShallowClone, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From, { let mut tx_verifier = tx_verifier_maker(storage_backend, chain_config.shallow_clone()); @@ -113,20 +115,21 @@ impl TransactionVerificationStrategy for DefaultTransactionVerificationStrategy Ok(tx_verifier) } - fn disconnect_block( + fn disconnect_block( &self, tx_verifier_maker: M, storage_backend: S, chain_config: C, block: &WithId, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From, { let mut tx_verifier = tx_verifier_maker(storage_backend, chain_config); diff --git a/chainstate/src/detail/tx_verification_strategy/mod.rs b/chainstate/src/detail/tx_verification_strategy/mod.rs index f2bcf4477a..c793a9dcfb 100644 --- a/chainstate/src/detail/tx_verification_strategy/mod.rs +++ b/chainstate/src/detail/tx_verification_strategy/mod.rs @@ -21,6 +21,7 @@ use common::{ chain::{block::timestamp::BlockTimestamp, Block, ChainConfig}, primitives::id::WithId, }; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use tokens_accounting::TokensAccountingView; use tx_verifier::{ @@ -34,13 +35,13 @@ use utils::shallow_clone::ShallowClone; use utxo::UtxosView; // TODO: replace with trait_alias when stabilized -pub trait TransactionVerifierMakerFn: - Fn(S, C) -> TransactionVerifier +pub trait TransactionVerifierMakerFn: + Fn(S, C) -> TransactionVerifier { } -impl TransactionVerifierMakerFn for F where - F: Fn(S, C) -> TransactionVerifier +impl TransactionVerifierMakerFn for F where + F: Fn(S, C) -> TransactionVerifier { } @@ -53,7 +54,7 @@ pub trait TransactionVerificationStrategy: Sized + Send { /// state. It just returns a TransactionVerifier that can be /// used to update the database/storage state. #[allow(clippy::too_many_arguments)] - fn connect_block( + fn connect_block( &self, tx_verifier_maker: M, storage_backend: S, @@ -61,14 +62,15 @@ pub trait TransactionVerificationStrategy: Sized + Send { block_index: &BlockIndex, block: &WithId, median_time_past: BlockTimestamp, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where S: TransactionVerifierStorageRef, U: UtxosView, C: AsRef + ShallowClone, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From; /// Disconnect the transactions given by block and block_index, @@ -77,19 +79,20 @@ pub trait TransactionVerificationStrategy: Sized + Send { /// Notice that this doesn't modify the internal database/storage /// state. It just returns a TransactionVerifier that can be /// used to update the database/storage state. - fn disconnect_block( + fn disconnect_block( &self, tx_verifier_maker: M, storage_backend: S, chain_config: C, block: &WithId, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From; } diff --git a/chainstate/storage/Cargo.toml b/chainstate/storage/Cargo.toml index d8f3205140..5a373cc21c 100644 --- a/chainstate/storage/Cargo.toml +++ b/chainstate/storage/Cargo.toml @@ -12,6 +12,7 @@ accounting = { path = "../../accounting" } chainstate-types = { path = "../types" } common = { path = "../../common" } logging = { path = "../../logging" } +orders-accounting = { path = "../../orders-accounting" } pos-accounting = { path = "../../pos-accounting" } randomness = { path = "../../randomness" } serialization = { path = "../../serialization" } diff --git a/chainstate/storage/src/internal/store_tx/read_impls.rs b/chainstate/storage/src/internal/store_tx/read_impls.rs index 3322e8d666..04fc373875 100644 --- a/chainstate/storage/src/internal/store_tx/read_impls.rs +++ b/chainstate/storage/src/internal/store_tx/read_impls.rs @@ -22,11 +22,12 @@ use common::{ block::{signed_block_header::SignedBlockHeader, BlockReward}, config::{EpochIndex, MagicBytes}, tokens::{TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, Block, DelegationId, GenBlock, PoolId, Transaction, - UtxoOutPoint, + AccountNonce, AccountType, Block, DelegationId, GenBlock, OrderData, OrderId, PoolId, + Transaction, UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id, H256}, }; +use orders_accounting::OrdersAccountingStorageRead; use pos_accounting::{ DelegationData, DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingStorageRead, PoSAccountingUndo, PoolData, @@ -365,6 +366,25 @@ impl<'st, B: storage::Backend> TokensAccountingStorageRead for super::StoreTxRo< } } +impl<'st, B: storage::Backend> OrdersAccountingStorageRead for super::StoreTxRo<'st, B> { + type Error = crate::Error; + + #[log_error] + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + #[log_error] + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + #[log_error] + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } +} + /// Blockchain data storage transaction impl<'st, B: storage::Backend> BlockchainStorageRead for super::StoreTxRw<'st, B> { #[log_error] @@ -650,3 +670,22 @@ impl<'st, B: storage::Backend> TokensAccountingStorageRead for super::StoreTxRw< self.read::(id) } } + +impl<'st, B: storage::Backend> OrdersAccountingStorageRead for super::StoreTxRw<'st, B> { + type Error = crate::Error; + + #[log_error] + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + #[log_error] + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + #[log_error] + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } +} diff --git a/chainstate/storage/src/internal/store_tx/write_impls.rs b/chainstate/storage/src/internal/store_tx/write_impls.rs index 70b1b045e8..98f61b0976 100644 --- a/chainstate/storage/src/internal/store_tx/write_impls.rs +++ b/chainstate/storage/src/internal/store_tx/write_impls.rs @@ -20,11 +20,12 @@ use common::{ chain::{ config::{EpochIndex, MagicBytes}, tokens::{TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, Block, DelegationId, GenBlock, PoolId, Transaction, - UtxoOutPoint, + AccountNonce, AccountType, Block, DelegationId, GenBlock, OrderData, OrderId, PoolId, + Transaction, UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id, Idable}, }; +use orders_accounting::OrdersAccountingStorageWrite; use pos_accounting::{ DelegationData, DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingStorageWrite, PoSAccountingUndo, PoolData, @@ -402,3 +403,35 @@ impl<'st, B: storage::Backend> TokensAccountingStorageWrite for StoreTxRw<'st, B self.del::(id) } } + +impl<'st, B: storage::Backend> OrdersAccountingStorageWrite for StoreTxRw<'st, B> { + #[log_error] + fn set_order_data(&mut self, id: &OrderId, data: &OrderData) -> crate::Result<()> { + todo!() + } + + #[log_error] + fn del_order_data(&mut self, id: &OrderId) -> crate::Result<()> { + todo!() + } + + #[log_error] + fn set_ask_balance(&mut self, id: &OrderId, balance: &Amount) -> crate::Result<()> { + todo!() + } + + #[log_error] + fn del_ask_balance(&mut self, id: &OrderId) -> crate::Result<()> { + todo!() + } + + #[log_error] + fn set_give_balance(&mut self, id: &OrderId, balance: &Amount) -> crate::Result<()> { + todo!() + } + + #[log_error] + fn del_give_balance(&mut self, id: &OrderId) -> crate::Result<()> { + todo!() + } +} diff --git a/chainstate/storage/src/lib.rs b/chainstate/storage/src/lib.rs index 91c61fa71e..d12ab11c2b 100644 --- a/chainstate/storage/src/lib.rs +++ b/chainstate/storage/src/lib.rs @@ -34,6 +34,7 @@ use common::{ }, primitives::{BlockHeight, Id}, }; +use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingStorageWrite}; use pos_accounting::{ DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingStorageRead, PoSAccountingStorageWrite, PoSAccountingUndo, @@ -66,6 +67,7 @@ pub trait BlockchainStorageRead: + PoSAccountingStorageRead + EpochStorageRead + TokensAccountingStorageRead + + OrdersAccountingStorageRead { // TODO: below (and in lots of other places too) Id is sometimes passes by ref and sometimes // by value. It's better to choose one "canonical" approach and use it everywhere. @@ -163,6 +165,7 @@ pub trait BlockchainStorageWrite: + PoSAccountingStorageWrite + EpochStorageWrite + TokensAccountingStorageWrite + + OrdersAccountingStorageWrite { /// Set storage version fn set_storage_version(&mut self, version: ChainstateStorageVersion) -> Result<()>; diff --git a/chainstate/storage/src/mock/mock_impl.rs b/chainstate/storage/src/mock/mock_impl.rs index e1769e9e8b..5846e98ceb 100644 --- a/chainstate/storage/src/mock/mock_impl.rs +++ b/chainstate/storage/src/mock/mock_impl.rs @@ -24,10 +24,12 @@ use common::{ config::{EpochIndex, MagicBytes}, tokens::{TokenAuxiliaryData, TokenId}, transaction::Transaction, - AccountNonce, AccountType, Block, DelegationId, GenBlock, PoolId, UtxoOutPoint, + AccountNonce, AccountType, Block, DelegationId, GenBlock, OrderData, OrderId, PoolId, + UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id}, }; +use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingStorageWrite}; use pos_accounting::{ DelegationData, DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingUndo, PoolData, }; @@ -160,6 +162,13 @@ mockall::mock! { fn get_circulating_supply(&self, id: &TokenId,) -> crate::Result >; } + impl OrdersAccountingStorageRead for Store { + type Error = crate::Error; + fn get_order_data(&self, id: &OrderId) -> crate::Result>; + fn get_ask_balance(&self, id: &OrderId) -> crate::Result>; + fn get_give_balance(&self, id: &OrderId) -> crate::Result>; + } + impl crate::BlockchainStorageWrite for Store { fn set_storage_version(&mut self, version: ChainstateStorageVersion) -> crate::Result<()>; fn set_magic_bytes(&mut self, bytes: &MagicBytes) -> crate::Result<()>; @@ -307,6 +316,17 @@ mockall::mock! { fn del_circulating_supply(&mut self, id: &TokenId) -> crate::Result<()>; } + impl OrdersAccountingStorageWrite for Store { + fn set_order_data(&mut self, id: &OrderId, data: &OrderData) -> crate::Result<()>; + fn del_order_data(&mut self, id: &OrderId) -> crate::Result<()>; + + fn set_ask_balance(&mut self, id: &OrderId, balance: &Amount) -> crate::Result<()>; + fn del_ask_balance(&mut self, id: &OrderId) -> crate::Result<()>; + + fn set_give_balance(&mut self, id: &OrderId, balance: &Amount) -> crate::Result<()>; + fn del_give_balance(&mut self, id: &OrderId) -> crate::Result<()>; + } + #[allow(clippy::extra_unused_lifetimes)] impl<'tx> crate::Transactional<'tx> for Store { type TransactionRo = MockStoreTxRo; @@ -430,6 +450,13 @@ mockall::mock! { fn get_circulating_supply(&self, id: &TokenId,) -> crate::Result >; } + impl OrdersAccountingStorageRead for StoreTxRo { + type Error = crate::Error; + fn get_order_data(&self, id: &OrderId) -> crate::Result>; + fn get_ask_balance(&self, id: &OrderId) -> crate::Result>; + fn get_give_balance(&self, id: &OrderId) -> crate::Result>; + } + impl crate::TransactionRo for StoreTxRo { fn close(self); } @@ -547,6 +574,13 @@ mockall::mock! { fn get_circulating_supply(&self, id: &TokenId,) -> crate::Result >; } + impl OrdersAccountingStorageRead for StoreTxRw { + type Error = crate::Error; + fn get_order_data(&self, id: &OrderId) -> crate::Result>; + fn get_ask_balance(&self, id: &OrderId) -> crate::Result>; + fn get_give_balance(&self, id: &OrderId) -> crate::Result>; + } + impl crate::BlockchainStorageWrite for StoreTxRw { fn set_storage_version(&mut self, version: ChainstateStorageVersion) -> crate::Result<()>; fn set_magic_bytes(&mut self, bytes: &MagicBytes) -> crate::Result<()>; @@ -694,6 +728,18 @@ mockall::mock! { fn del_circulating_supply(&mut self, id: &TokenId) -> crate::Result<()>; } + + impl OrdersAccountingStorageWrite for StoreTxRw { + fn set_order_data(&mut self, id: &OrderId, data: &OrderData) -> crate::Result<()>; + fn del_order_data(&mut self, id: &OrderId) -> crate::Result<()>; + + fn set_ask_balance(&mut self, id: &OrderId, balance: &Amount) -> crate::Result<()>; + fn del_ask_balance(&mut self, id: &OrderId) -> crate::Result<()>; + + fn set_give_balance(&mut self, id: &OrderId, balance: &Amount) -> crate::Result<()>; + fn del_give_balance(&mut self, id: &OrderId) -> crate::Result<()>; + } + impl crate::TransactionRw for StoreTxRw { fn abort(self); fn commit(self) -> crate::Result<()>; diff --git a/chainstate/test-framework/Cargo.toml b/chainstate/test-framework/Cargo.toml index 7c37d8e8f5..a2b0bab55f 100644 --- a/chainstate/test-framework/Cargo.toml +++ b/chainstate/test-framework/Cargo.toml @@ -15,6 +15,7 @@ common = { path = "../../common" } consensus = { path = "../../consensus" } constraints-value-accumulator = { path = "../constraints-value-accumulator" } crypto = { path = "../../crypto" } +orders-accounting = { path = "../../orders-accounting" } pos-accounting = { path = "../../pos-accounting" } randomness = { path = "../../randomness" } serialization = { path = "../../serialization" } diff --git a/chainstate/test-framework/src/tx_verification_strategy/disposable_strategy.rs b/chainstate/test-framework/src/tx_verification_strategy/disposable_strategy.rs index 6239a062eb..b6e1be89f9 100644 --- a/chainstate/test-framework/src/tx_verification_strategy/disposable_strategy.rs +++ b/chainstate/test-framework/src/tx_verification_strategy/disposable_strategy.rs @@ -22,6 +22,7 @@ use common::{ primitives::{id::WithId, Idable}, }; use constraints_value_accumulator::AccumulatedFee; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use tokens_accounting::TokensAccountingView; use tx_verifier::{ @@ -52,7 +53,7 @@ impl Default for DisposableTransactionVerificationStrategy { } impl TransactionVerificationStrategy for DisposableTransactionVerificationStrategy { - fn connect_block( + fn connect_block( &self, tx_verifier_maker: M, storage_backend: S, @@ -60,14 +61,15 @@ impl TransactionVerificationStrategy for DisposableTransactionVerificationStrate block_index: &BlockIndex, block: &WithId, median_time_past: BlockTimestamp, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef + ShallowClone, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From, { let mut base_tx_verifier = tx_verifier_maker(storage_backend, chain_config.shallow_clone()); @@ -121,20 +123,21 @@ impl TransactionVerificationStrategy for DisposableTransactionVerificationStrate Ok(base_tx_verifier) } - fn disconnect_block( + fn disconnect_block( &self, tx_verifier_maker: M, storage_backend: S, chain_config: C, block: &WithId, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From, { let mut base_tx_verifier = tx_verifier_maker(storage_backend, chain_config); diff --git a/chainstate/test-framework/src/tx_verification_strategy/randomized_strategy.rs b/chainstate/test-framework/src/tx_verification_strategy/randomized_strategy.rs index e175f9d65c..6c71cd0edf 100644 --- a/chainstate/test-framework/src/tx_verification_strategy/randomized_strategy.rs +++ b/chainstate/test-framework/src/tx_verification_strategy/randomized_strategy.rs @@ -22,6 +22,7 @@ use common::{ primitives::{id::WithId, Idable}, }; use constraints_value_accumulator::AccumulatedFee; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use randomness::{Rng, RngCore}; use test_utils::random::{make_seedable_rng, Seed}; @@ -64,7 +65,7 @@ impl RandomizedTransactionVerificationStrategy { } impl TransactionVerificationStrategy for RandomizedTransactionVerificationStrategy { - fn connect_block( + fn connect_block( &self, tx_verifier_maker: M, storage_backend: S, @@ -72,14 +73,15 @@ impl TransactionVerificationStrategy for RandomizedTransactionVerificationStrate block_index: &BlockIndex, block: &WithId, median_time_past: BlockTimestamp, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef + ShallowClone, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From, { let mut tx_verifier = self @@ -98,20 +100,21 @@ impl TransactionVerificationStrategy for RandomizedTransactionVerificationStrate Ok(tx_verifier) } - fn disconnect_block( + fn disconnect_block( &self, tx_verifier_maker: M, storage_backend: S, chain_config: C, block: &WithId, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From, { let mut tx_verifier = @@ -125,7 +128,7 @@ impl TransactionVerificationStrategy for RandomizedTransactionVerificationStrate impl RandomizedTransactionVerificationStrategy { #[allow(clippy::too_many_arguments, clippy::type_complexity)] - fn connect_with_base( + fn connect_with_base( &self, tx_verifier_maker: M, storage_backend: S, @@ -133,14 +136,15 @@ impl RandomizedTransactionVerificationStrategy { block_index: &BlockIndex, block: &WithId, median_time_past: &BlockTimestamp, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef + ShallowClone, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From, { let mut tx_verifier = tx_verifier_maker(storage_backend, chain_config.shallow_clone()); @@ -209,9 +213,9 @@ impl RandomizedTransactionVerificationStrategy { Ok(tx_verifier) } - fn connect_with_derived( + fn connect_with_derived( &self, - base_tx_verifier: &TransactionVerifier, + base_tx_verifier: &TransactionVerifier, block: &WithId, block_index: &BlockIndex, median_time_past: &BlockTimestamp, @@ -222,6 +226,7 @@ impl RandomizedTransactionVerificationStrategy { U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, + O: OrdersAccountingView, S: TransactionVerifierStorageRef, ::Error: From, { @@ -251,20 +256,21 @@ impl RandomizedTransactionVerificationStrategy { Ok((cache, total_fees, tx_num)) } - fn disconnect_with_base( + fn disconnect_with_base( &self, tx_verifier_maker: M, storage_backend: S, chain_config: C, block: &WithId, - ) -> Result, ConnectTransactionError> + ) -> Result, ConnectTransactionError> where C: AsRef, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - M: TransactionVerifierMakerFn, + O: OrdersAccountingView, + M: TransactionVerifierMakerFn, ::Error: From, { let mut tx_verifier = tx_verifier_maker(storage_backend, chain_config); @@ -296,9 +302,9 @@ impl RandomizedTransactionVerificationStrategy { Ok(tx_verifier) } - fn disconnect_with_derived( + fn disconnect_with_derived( &self, - base_tx_verifier: &TransactionVerifier, + base_tx_verifier: &TransactionVerifier, block: &WithId, mut tx_num: i32, ) -> Result<(TransactionVerifierDelta, i32), ConnectTransactionError> @@ -307,6 +313,7 @@ impl RandomizedTransactionVerificationStrategy { U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, + O: OrdersAccountingView, S: TransactionVerifierStorageRef, ::Error: From, { diff --git a/chainstate/test-suite/Cargo.toml b/chainstate/test-suite/Cargo.toml index 1094f6154e..148bebc9ce 100644 --- a/chainstate/test-suite/Cargo.toml +++ b/chainstate/test-suite/Cargo.toml @@ -18,6 +18,7 @@ consensus = { path = "../../consensus" } constraints-value-accumulator = { path = "../constraints-value-accumulator" } crypto = { path = "../../crypto" } logging = { path = "../../logging" } +orders-accounting = { path = "../../orders-accounting" } pos-accounting = { path = "../../pos-accounting" } randomness = { path = "../../randomness" } serialization = { path = "../../serialization" } diff --git a/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs b/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs index a7068cd180..f7b16a43f4 100644 --- a/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs +++ b/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs @@ -24,11 +24,12 @@ use chainstate_types::{storage_result, GenBlockIndex}; use common::{ chain::{ tokens::{TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, ChainConfig, DelegationId, GenBlock, GenBlockId, PoolId, - Transaction, + AccountNonce, AccountType, ChainConfig, DelegationId, GenBlock, GenBlockId, OrderData, + OrderId, PoolId, Transaction, }, primitives::{Amount, Id}, }; +use orders_accounting::OrdersAccountingStorageRead; use pos_accounting::{ DelegationData, PoSAccountingDB, PoSAccountingUndo, PoSAccountingView, PoolData, }; @@ -237,3 +238,19 @@ impl TokensAccountingStorageRead for InMemoryStorageWrapper { self.storage.transaction_ro().unwrap().get_circulating_supply(id) } } + +impl OrdersAccountingStorageRead for InMemoryStorageWrapper { + type Error = storage_result::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } +} diff --git a/chainstate/test-suite/src/tests/tx_verifier_among_threads.rs b/chainstate/test-suite/src/tests/tx_verifier_among_threads.rs index 21d2bebefd..bae2eb8492 100644 --- a/chainstate/test-suite/src/tests/tx_verifier_among_threads.rs +++ b/chainstate/test-suite/src/tests/tx_verifier_among_threads.rs @@ -16,9 +16,10 @@ use chainstate_test_framework::{TestFramework, TestStore}; use common::chain::config::Builder as ConfigBuilder; use common::{ - chain::{DelegationId, PoolId, UtxoOutPoint}, + chain::{DelegationId, OrderData, OrderId, PoolId, UtxoOutPoint}, primitives::{Id, H256}, }; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use rstest::rstest; use test_utils::random::{make_seedable_rng, Seed}; @@ -126,6 +127,30 @@ impl TokensAccountingView for EmptyTokensAccountingView { } } +struct EmptyOrdersAccountingView; + +impl OrdersAccountingView for EmptyOrdersAccountingView { + type Error = orders_accounting::Error; + + fn get_order_data(&self, _id: &OrderId) -> Result, Self::Error> { + Ok(None) + } + + fn get_ask_balance( + &self, + _id: &OrderId, + ) -> Result, Self::Error> { + Ok(None) + } + + fn get_give_balance( + &self, + _id: &OrderId, + ) -> Result, Self::Error> { + Ok(None) + } +} + /// This test proves that a transaction verifier with this structure can be moved among threads #[rstest] #[trace] @@ -142,6 +167,7 @@ fn transfer_tx_verifier_to_thread(#[case] seed: Seed) { let utxos = EmptyUtxosView {}; let accounting = EmptyAccountingView {}; let tokens_accounting = EmptyTokensAccountingView {}; + let orders_accounting = EmptyOrdersAccountingView {}; let verifier = TransactionVerifier::new_generic( &storage, @@ -149,6 +175,7 @@ fn transfer_tx_verifier_to_thread(#[case] seed: Seed) { utxos, accounting, tokens_accounting, + orders_accounting, ); std::thread::scope(|s| { diff --git a/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs b/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs index 3d06877899..9c7a7fd2ea 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs @@ -29,10 +29,15 @@ use chainstate_types::{storage_result, GenBlockIndex}; use common::{ chain::{ tokens::{TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, DelegationId, GenBlock, PoolId, Transaction, UtxoOutPoint, + AccountNonce, AccountType, DelegationId, GenBlock, OrderData, OrderId, PoolId, Transaction, + UtxoOutPoint, }, primitives::{Amount, Id}, }; +use orders_accounting::{ + FlushableOrdersAccountingView, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData, + OrdersAccountingStorageRead, OrdersAccountingView, +}; use pos_accounting::{ DelegationData, DeltaMergeUndo, FlushablePoSAccountingView, PoSAccountingDeltaData, PoSAccountingUndo, PoSAccountingView, PoolData, @@ -49,7 +54,8 @@ impl< U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, - > TransactionVerifierStorageRef for TransactionVerifier + O: OrdersAccountingView, + > TransactionVerifierStorageRef for TransactionVerifier where ::Error: From, { @@ -141,8 +147,8 @@ where } } -impl UtxosStorageRead - for TransactionVerifier +impl UtxosStorageRead + for TransactionVerifier where ::Error: From, { @@ -157,7 +163,7 @@ where } } -impl TransactionVerifierStorageMut for TransactionVerifier +impl TransactionVerifierStorageMut for TransactionVerifier where S: TransactionVerifierStorageRef, ::Error: From, @@ -165,6 +171,7 @@ where U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, + O: OrdersAccountingView, { fn set_token_aux_data( &mut self, @@ -290,9 +297,7 @@ where } } -impl FlushableUtxoView - for TransactionVerifier -{ +impl FlushableUtxoView for TransactionVerifier { type Error = utxo::Error; fn batch_write(&mut self, utxos: ConsumedUtxoCache) -> Result<(), utxo::Error> { @@ -300,13 +305,8 @@ impl } } -impl< - C, - S: TransactionVerifierStorageRef, - U: UtxosView, - A: PoSAccountingView, - T: TokensAccountingView, - > PoSAccountingView for TransactionVerifier +impl PoSAccountingView + for TransactionVerifier { type Error = pos_accounting::Error; @@ -360,8 +360,8 @@ impl< } } -impl FlushablePoSAccountingView - for TransactionVerifier +impl FlushablePoSAccountingView + for TransactionVerifier { fn batch_write_delta( &mut self, @@ -371,13 +371,8 @@ impl FlushablePoSAccountingView } } -impl< - C, - S: TransactionVerifierStorageRef, - U: UtxosView, - A: PoSAccountingView, - T: TokensAccountingView, - > TokensAccountingStorageRead for TransactionVerifier +impl TokensAccountingStorageRead + for TransactionVerifier { type Error = tokens_accounting::Error; @@ -393,9 +388,7 @@ impl< } } -impl FlushableTokensAccountingView - for TransactionVerifier -{ +impl FlushableTokensAccountingView for TransactionVerifier { type Error = tokens_accounting::Error; fn batch_write_tokens_data( @@ -405,3 +398,32 @@ impl FlushableTokensAccountingView self.tokens_accounting_cache.batch_write_tokens_data(data) } } + +impl OrdersAccountingStorageRead + for TransactionVerifier +{ + type Error = orders_accounting::Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + self.orders_accounting_cache.get_order_data(id) + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.orders_accounting_cache.get_ask_balance(id) + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + self.orders_accounting_cache.get_give_balance(id) + } +} + +impl FlushableOrdersAccountingView for TransactionVerifier { + type Error = orders_accounting::Error; + + fn batch_write_orders_data( + &mut self, + data: OrdersAccountingDeltaData, + ) -> Result { + self.orders_accounting_cache.batch_write_orders_data(data) + } +} diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 16cea43531..61d53d6785 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -30,6 +30,10 @@ pub mod tokens_check; mod tx_source; use accounting::BlockRewardUndo; use constraints_value_accumulator::AccumulatedFee; +use orders_accounting::{ + OrdersAccountingCache, OrdersAccountingDB, OrdersAccountingDeltaData, OrdersAccountingUndo, + OrdersAccountingView, +}; use tokens_accounting::{ TokenAccountingUndo, TokensAccountingCache, TokensAccountingDB, TokensAccountingDeltaData, TokensAccountingOperations, TokensAccountingStorageRead, TokensAccountingView, @@ -92,6 +96,9 @@ pub struct TransactionVerifierDelta { tokens_accounting_delta: TokensAccountingDeltaData, tokens_accounting_delta_undo: BTreeMap>, + orders_accounting_delta: OrdersAccountingDeltaData, + orders_accounting_delta_undo: + BTreeMap>, } impl TransactionVerifierDelta { @@ -101,7 +108,7 @@ impl TransactionVerifierDelta { } /// The tool used to verify transactions and cache their updated states in memory -pub struct TransactionVerifier { +pub struct TransactionVerifier { chain_config: C, storage: S, best_block: Id, @@ -117,11 +124,14 @@ pub struct TransactionVerifier { tokens_accounting_cache: TokensAccountingCache, tokens_accounting_block_undo: AccountingBlockUndoCache, + orders_accounting_cache: OrdersAccountingCache, + orders_accounting_block_undo: AccountingBlockUndoCache, + account_nonce: BTreeMap>, } impl - TransactionVerifier, S, TokensAccountingDB> + TransactionVerifier, S, TokensAccountingDB, OrdersAccountingDB> { pub fn new(storage: S, chain_config: C) -> Self { let accounting_delta_adapter = PoSAccountingDeltaAdapter::new(storage.shallow_clone()); @@ -132,6 +142,8 @@ impl .expect("Database error while reading utxos best block"); let tokens_accounting_cache = TokensAccountingCache::new(TokensAccountingDB::new(storage.shallow_clone())); + let orders_accounting_cache = + OrdersAccountingCache::new(OrdersAccountingDB::new(storage.shallow_clone())); Self { storage, chain_config, @@ -143,17 +155,20 @@ impl pos_accounting_block_undo: AccountingBlockUndoCache::::new(), tokens_accounting_cache, tokens_accounting_block_undo: AccountingBlockUndoCache::::new(), + orders_accounting_cache, + orders_accounting_block_undo: AccountingBlockUndoCache::::new(), account_nonce: BTreeMap::new(), } } } -impl TransactionVerifier +impl TransactionVerifier where S: TransactionVerifierStorageRef, U: UtxosView + Send + Sync, A: PoSAccountingView + Send + Sync, T: TokensAccountingView + Send + Sync, + O: OrdersAccountingView + Send + Sync, { pub fn new_generic( storage: S, @@ -161,6 +176,7 @@ where utxos: U, accounting: A, tokens_accounting: T, + orders_accounting: O, ) -> Self { // TODO: both "expect"s in this function may fire when exiting the node-gui app; // get rid of them and return a proper Result. @@ -179,18 +195,21 @@ where pos_accounting_block_undo: AccountingBlockUndoCache::::new(), tokens_accounting_cache: TokensAccountingCache::new(tokens_accounting), tokens_accounting_block_undo: AccountingBlockUndoCache::::new(), + orders_accounting_cache: OrdersAccountingCache::new(orders_accounting), + orders_accounting_block_undo: AccountingBlockUndoCache::::new(), account_nonce: BTreeMap::new(), } } } -impl TransactionVerifier +impl TransactionVerifier where C: AsRef, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView, T: TokensAccountingView, + O: OrdersAccountingView, ::Error: From, { pub fn derive_child( @@ -201,6 +220,7 @@ where &UtxosCache, &PoSAccountingDelta, &TokensAccountingCache, + &OrdersAccountingCache, > { TransactionVerifier { storage: self, @@ -214,6 +234,8 @@ where pos_accounting_block_undo: AccountingBlockUndoCache::::new(), tokens_accounting_cache: TokensAccountingCache::new(&self.tokens_accounting_cache), tokens_accounting_block_undo: AccountingBlockUndoCache::::new(), + orders_accounting_cache: OrdersAccountingCache::new(&self.orders_accounting_cache), + orders_accounting_block_undo: AccountingBlockUndoCache::::new(), best_block: self.best_block, account_nonce: BTreeMap::new(), } @@ -994,6 +1016,8 @@ where account_nonce: self.account_nonce, tokens_accounting_delta: self.tokens_accounting_cache.consume(), tokens_accounting_delta_undo: self.tokens_accounting_block_undo.consume(), + orders_accounting_delta: self.orders_accounting_cache.consume(), + orders_accounting_delta_undo: self.orders_accounting_block_undo.consume(), }) } } diff --git a/chainstate/tx-verifier/src/transaction_verifier/storage.rs b/chainstate/tx-verifier/src/transaction_verifier/storage.rs index b480c9126e..fe5109a65d 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/storage.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/storage.rs @@ -23,6 +23,7 @@ use common::{ }, primitives::Id, }; +use orders_accounting::{FlushableOrdersAccountingView, OrdersAccountingStorageRead}; use pos_accounting::{ FlushablePoSAccountingView, PoSAccountingDeltaData, PoSAccountingUndo, PoSAccountingView, }; @@ -64,7 +65,7 @@ pub enum TransactionVerifierStorageError { // TODO(Gosha): PoSAccountingView should be replaced with PoSAccountingStorageRead in which the // return error type can handle both storage_result::Error and pos_accounting::Error pub trait TransactionVerifierStorageRef: - UtxosStorageRead + PoSAccountingView + TokensAccountingStorageRead + UtxosStorageRead + PoSAccountingView + TokensAccountingStorageRead + OrdersAccountingStorageRead { type Error: std::error::Error; @@ -113,6 +114,8 @@ pub trait TransactionVerifierStorageRef: &self, account: AccountType, ) -> Result, ::Error>; + + // FIXME: add methods for order undo } pub trait TransactionVerifierStorageMut: @@ -120,6 +123,7 @@ pub trait TransactionVerifierStorageMut: + FlushableUtxoView + FlushablePoSAccountingView + FlushableTokensAccountingView + + FlushableOrdersAccountingView { fn set_token_aux_data( &mut self, diff --git a/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs b/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs index 6640bb5829..2794ac2257 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs @@ -29,10 +29,12 @@ use chainstate_types::{storage_result, GenBlockIndex}; use common::{ chain::{ tokens::{TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, DelegationId, GenBlock, PoolId, Transaction, UtxoOutPoint, + AccountNonce, AccountType, DelegationId, GenBlock, OrderData, OrderId, PoolId, Transaction, + UtxoOutPoint, }, primitives::{Amount, Id}, }; +use orders_accounting::{FlushableOrdersAccountingView, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData, OrdersAccountingStorageRead}; use pos_accounting::{ DelegationData, DeltaMergeUndo, FlushablePoSAccountingView, PoSAccountingDeltaData, PoSAccountingUndo, PoSAccountingView, PoolData, @@ -196,4 +198,19 @@ mockall::mock! { type Error = tokens_accounting::Error; fn batch_write_tokens_data(&mut self, delta: TokensAccountingDeltaData) -> Result; } + + impl OrdersAccountingStorageRead for Store { + type Error = orders_accounting::Error; + fn get_order_data(&self, id: &OrderId) -> Result, orders_accounting::Error>; + fn get_ask_balance(&self, id: &OrderId) -> Result, orders_accounting::Error>; + fn get_give_balance(&self, id: &OrderId) -> Result, orders_accounting::Error>; + } + + impl FlushableOrdersAccountingView for Store { + type Error = orders_accounting::Error; + fn batch_write_orders_data( + &mut self, + data: OrdersAccountingDeltaData, + ) -> Result; + } } diff --git a/mempool/Cargo.toml b/mempool/Cargo.toml index 1fa5b6d975..ba0b5012ba 100644 --- a/mempool/Cargo.toml +++ b/mempool/Cargo.toml @@ -16,6 +16,7 @@ crypto = { path = "../crypto" } logging = { path = "../logging" } mempool-types = { path = "types" } mintscript = { path = "../mintscript" } +orders-accounting = { path = "../orders-accounting" } p2p-types = { path = "../p2p/types" } pos-accounting = { path = "../pos-accounting" } rpc = { path = "../rpc" } diff --git a/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs b/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs index 3813037f5b..8cd86f022f 100644 --- a/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs +++ b/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs @@ -27,10 +27,12 @@ use chainstate_types::storage_result; use common::{ chain::{ tokens::{TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, DelegationId, GenBlock, PoolId, Transaction, UtxoOutPoint, + AccountNonce, AccountType, DelegationId, GenBlock, OrderData, OrderId, PoolId, Transaction, + UtxoOutPoint, }, primitives::{Amount, Id}, }; +use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingView}; use pos_accounting::{DelegationData, PoSAccountingUndo, PoSAccountingView, PoolData}; use subsystem::blocking::BlockingHandle; use tokens_accounting::{TokenAccountingUndo, TokensAccountingStorageRead, TokensAccountingView}; @@ -292,3 +294,35 @@ impl TokensAccountingStorageRead for ChainstateHandle { self.call(move |c| c.get_token_circulating_supply(&id)) } } + +impl OrdersAccountingView for ChainstateHandle { + type Error = Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } +} + +impl OrdersAccountingStorageRead for ChainstateHandle { + type Error = Error; + + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } + + fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { + todo!() + } +} diff --git a/mempool/src/pool/tx_pool/tx_verifier/mod.rs b/mempool/src/pool/tx_pool/tx_verifier/mod.rs index d606f713d8..23eb0b5688 100644 --- a/mempool/src/pool/tx_pool/tx_verifier/mod.rs +++ b/mempool/src/pool/tx_pool/tx_verifier/mod.rs @@ -36,6 +36,7 @@ pub type TransactionVerifier = chainstate::tx_verifier::TransactionVerifier< ChainstateHandle, ChainstateHandle, ChainstateHandle, + ChainstateHandle, >; /// Make a new transaction verifier @@ -49,6 +50,7 @@ pub fn create( chain_config, chainstate.shallow_clone(), chainstate.shallow_clone(), + chainstate.shallow_clone(), chainstate, ) } diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 140293e8d0..62159f8687 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -29,6 +29,7 @@ use crate::{ WithdrawOrderUndo, }, view::OrdersAccountingView, + FlushableOrdersAccountingView, OrdersAccountingDeltaUndoData, }; pub struct OrdersAccountingCache

{ @@ -246,3 +247,14 @@ impl OrdersAccountingOperations for OrdersAccountingCac } } } + +impl

FlushableOrdersAccountingView for OrdersAccountingCache

{ + type Error = Error; + + fn batch_write_orders_data( + &mut self, + delta: OrdersAccountingDeltaData, + ) -> Result { + self.data.merge_with_delta(delta) + } +} diff --git a/orders-accounting/src/data.rs b/orders-accounting/src/data.rs index 0b6765273b..3b6e781525 100644 --- a/orders-accounting/src/data.rs +++ b/orders-accounting/src/data.rs @@ -22,6 +22,8 @@ use common::{ }; use serialization::{Decode, Encode}; +use crate::error::Result; + #[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)] pub struct OrdersAccountingData { pub order_data: BTreeMap, @@ -46,6 +48,27 @@ pub struct OrdersAccountingDeltaData { pub(crate) give_balances: DeltaAmountCollection, } +impl OrdersAccountingDeltaData { + pub fn merge_with_delta( + &mut self, + other: OrdersAccountingDeltaData, + ) -> Result { + let order_data_undo = self.order_data.merge_delta_data(other.order_data)?; + + let ask_balance_undo = other.ask_balances.clone(); + self.ask_balances.merge_delta_amounts(other.ask_balances)?; + + let give_balance_undo = other.give_balances.clone(); + self.give_balances.merge_delta_amounts(other.give_balances)?; + + Ok(OrdersAccountingDeltaUndoData { + order_data: order_data_undo, + ask_balances: ask_balance_undo, + give_balances: give_balance_undo, + }) + } +} + impl OrdersAccountingDeltaData { pub fn new() -> Self { Self { diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index 5bb4f71c90..96f66e705d 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -23,10 +23,15 @@ mod view; pub use { cache::OrdersAccountingCache, + data::{OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}, error::Error, + operations::{OrdersAccountingOperations, OrdersAccountingUndo}, price_calculation::calculate_fill_order, - storage::{db::OrdersAccountingDB, in_memory::InMemoryOrdersAccounting}, - view::OrdersAccountingView, + storage::{ + db::OrdersAccountingDB, in_memory::InMemoryOrdersAccounting, OrdersAccountingStorageRead, + OrdersAccountingStorageWrite, + }, + view::{FlushableOrdersAccountingView, OrdersAccountingView}, }; #[cfg(test)] From 0124113c82b96efe0f7ccb1e2183584411a65b93 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Thu, 2 May 2024 10:40:11 +0300 Subject: [PATCH 11/37] Integrate into tx-verifier --- chainstate/src/detail/ban_score.rs | 31 +++++ .../chainstateref/tx_verifier_storage.rs | 59 ++++++++- chainstate/src/detail/error_classification.rs | 31 +++++ .../src/internal/store_tx/read_impls.rs | 18 ++- .../src/internal/store_tx/write_impls.rs | 16 ++- chainstate/storage/src/lib.rs | 20 ++- chainstate/storage/src/mock/mock_impl.rs | 27 +++- .../helpers/in_memory_storage_wrapper.rs | 27 +++- .../src/transaction_verifier/error.rs | 3 +- .../src/transaction_verifier/hierarchy.rs | 34 ++++- .../src/transaction_verifier/mod.rs | 119 +++++++++++++++++- .../src/transaction_verifier/storage.rs | 33 ++++- .../src/transaction_verifier/tests/mock.rs | 21 +++- common/src/chain/mod.rs | 2 +- common/src/chain/order.rs | 8 +- mempool/src/error/ban_score.rs | 32 +++++ mempool/src/pool/orphans/detect.rs | 1 + .../tx_pool/tx_verifier/chainstate_handle.rs | 9 +- 18 files changed, 471 insertions(+), 20 deletions(-) diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index a79be69d62..29694b36b2 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -140,6 +140,7 @@ impl BanScore for ConnectTransactionError { ConnectTransactionError::RewardDistributionError(err) => err.ban_score(), ConnectTransactionError::CheckTransactionError(err) => err.ban_score(), ConnectTransactionError::InputCheck(e) => e.ban_score(), + ConnectTransactionError::OrdersAccountingError(err) => err.ban_score(), } } } @@ -645,4 +646,34 @@ impl BanScore for RewardDistributionError { } } +impl BanScore for orders_accounting::Error { + fn ban_score(&self) -> u32 { + use orders_accounting::Error; + match self { + Error::StorageError(_) => todo!(), + Error::AccountingError(_) => todo!(), + Error::OrderAlreadyExists(_) => todo!(), + Error::OrderDataNotFound(_) => todo!(), + Error::OrderAskBalanceNotFound(_) => todo!(), + Error::OrderGiveBalanceNotFound(_) => todo!(), + Error::InvariantOrderDataNotFoundForUndo(_) => todo!(), + Error::InvariantOrderAskBalanceNotFoundForUndo(_) => todo!(), + Error::InvariantOrderAskBalanceChangedForUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceChangedForUndo(_) => todo!(), + Error::InvariantOrderDataExistForWithdrawUndo(_) => todo!(), + Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => todo!(), + Error::FillOrderChangeLeft(_) => todo!(), + Error::CurrencyMismatch => todo!(), + Error::OrderOverflow(_) => todo!(), + Error::AttemptedWithdrawNonexistingOrderData(_) => todo!(), + Error::AttemptedWithdrawNonexistingAskBalance(_) => todo!(), + Error::AttemptedWithdrawNonexistingGiveBalance(_) => todo!(), + Error::ViewFail => todo!(), + Error::StorageWrite => todo!(), + } + } +} + // TODO: tests in which we simulate every possible case and test the score diff --git a/chainstate/src/detail/chainstateref/tx_verifier_storage.rs b/chainstate/src/detail/chainstateref/tx_verifier_storage.rs index 3d34be1d0a..a115c60a1f 100644 --- a/chainstate/src/detail/chainstateref/tx_verifier_storage.rs +++ b/chainstate/src/detail/chainstateref/tx_verifier_storage.rs @@ -33,7 +33,9 @@ use common::{ }, primitives::{Amount, Id}, }; -use orders_accounting::{FlushableOrdersAccountingView, OrdersAccountingStorageRead}; +use orders_accounting::{ + FlushableOrdersAccountingView, OrdersAccountingStorageRead, OrdersAccountingUndo, +}; use pos_accounting::{ DelegationData, DeltaMergeUndo, FlushablePoSAccountingView, PoSAccountingDB, PoSAccountingDeltaData, PoSAccountingUndo, PoSAccountingView, PoolData, @@ -139,6 +141,26 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Transacti .get_account_nonce_count(account) .map_err(TransactionVerifierStorageError::from) } + + #[log_error] + fn get_orders_accounting_undo( + &self, + tx_source: TransactionSource, + ) -> Result>, TransactionVerifierStorageError> + { + match tx_source { + TransactionSource::Chain(id) => { + let undo = self + .db_tx + .get_orders_accounting_undo(id)? + .map(CachedBlockUndo::from_block_undo); + Ok(undo) + } + TransactionSource::Mempool => { + panic!("Mempool should not undo stuff in chainstate") + } + } + } } // TODO: this function is a duplicate of one in chainstate-types; the cause for this is that BlockchainStorageRead causes a circular dependencies @@ -389,6 +411,41 @@ impl<'a, S: BlockchainStorageWrite, V: TransactionVerificationStrategy> } } } + + #[log_error] + fn set_orders_accounting_undo_data( + &mut self, + tx_source: TransactionSource, + undo: &CachedBlockUndo, + ) -> Result<(), TransactionVerifierStorageError> { + // TODO: check tx_source at compile-time (mintlayer/mintlayer-core#633) + match tx_source { + TransactionSource::Chain(id) => self + .db_tx + .set_orders_accounting_undo_data(id, &undo.clone().consume()) + .map_err(TransactionVerifierStorageError::from), + TransactionSource::Mempool => { + panic!("Flushing mempool info into the storage is forbidden") + } + } + } + + #[log_error] + fn del_orders_accounting_undo_data( + &mut self, + tx_source: TransactionSource, + ) -> Result<(), TransactionVerifierStorageError> { + // TODO: check tx_source at compile-time (mintlayer/mintlayer-core#633) + match tx_source { + TransactionSource::Chain(id) => self + .db_tx + .del_orders_accounting_undo_data(id) + .map_err(TransactionVerifierStorageError::from), + TransactionSource::Mempool => { + panic!("Flushing mempool info into the storage is forbidden") + } + } + } } impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> PoSAccountingView diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index 7f04b1752a..3fe4f9edfd 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -316,6 +316,7 @@ impl BlockProcessingErrorClassification for ConnectTransactionError { ConnectTransactionError::PoSAccountingError(err) => err.classify(), ConnectTransactionError::ConstrainedValueAccumulatorError(err, _) => err.classify(), ConnectTransactionError::InputCheck(err) => err.classify(), + ConnectTransactionError::OrdersAccountingError(err) => err.classify(), } } } @@ -878,3 +879,33 @@ impl BlockProcessingErrorClassification for constraints_value_accumulator::Error } } } + +impl BlockProcessingErrorClassification for orders_accounting::Error { + fn classify(&self) -> BlockProcessingErrorClass { + use orders_accounting::Error; + match self { + Error::StorageError(_) => todo!(), + Error::AccountingError(_) => todo!(), + Error::OrderAlreadyExists(_) => todo!(), + Error::OrderDataNotFound(_) => todo!(), + Error::OrderAskBalanceNotFound(_) => todo!(), + Error::OrderGiveBalanceNotFound(_) => todo!(), + Error::InvariantOrderDataNotFoundForUndo(_) => todo!(), + Error::InvariantOrderAskBalanceNotFoundForUndo(_) => todo!(), + Error::InvariantOrderAskBalanceChangedForUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceChangedForUndo(_) => todo!(), + Error::InvariantOrderDataExistForWithdrawUndo(_) => todo!(), + Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => todo!(), + Error::FillOrderChangeLeft(_) => todo!(), + Error::CurrencyMismatch => todo!(), + Error::OrderOverflow(_) => todo!(), + Error::AttemptedWithdrawNonexistingOrderData(_) => todo!(), + Error::AttemptedWithdrawNonexistingAskBalance(_) => todo!(), + Error::AttemptedWithdrawNonexistingGiveBalance(_) => todo!(), + Error::ViewFail => todo!(), + Error::StorageWrite => todo!(), + } + } +} diff --git a/chainstate/storage/src/internal/store_tx/read_impls.rs b/chainstate/storage/src/internal/store_tx/read_impls.rs index 04fc373875..d532b0a8a6 100644 --- a/chainstate/storage/src/internal/store_tx/read_impls.rs +++ b/chainstate/storage/src/internal/store_tx/read_impls.rs @@ -27,7 +27,7 @@ use common::{ }, primitives::{Amount, BlockHeight, Id, H256}, }; -use orders_accounting::OrdersAccountingStorageRead; +use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingUndo}; use pos_accounting::{ DelegationData, DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingStorageRead, PoSAccountingUndo, PoolData, @@ -169,6 +169,14 @@ impl<'st, B: storage::Backend> BlockchainStorageRead for super::StoreTxRo<'st, B self.read::(&id) } + #[log_error] + fn get_orders_accounting_undo( + &self, + id: Id, + ) -> crate::Result>> { + todo!() + } + #[log_error] fn get_block_tree_by_height( &self, @@ -469,6 +477,14 @@ impl<'st, B: storage::Backend> BlockchainStorageRead for super::StoreTxRw<'st, B self.read::(&id) } + #[log_error] + fn get_orders_accounting_undo( + &self, + id: Id, + ) -> crate::Result>> { + todo!() + } + #[log_error] fn get_block_tree_by_height( &self, diff --git a/chainstate/storage/src/internal/store_tx/write_impls.rs b/chainstate/storage/src/internal/store_tx/write_impls.rs index 98f61b0976..4bb49694f2 100644 --- a/chainstate/storage/src/internal/store_tx/write_impls.rs +++ b/chainstate/storage/src/internal/store_tx/write_impls.rs @@ -25,7 +25,7 @@ use common::{ }, primitives::{Amount, BlockHeight, Id, Idable}, }; -use orders_accounting::OrdersAccountingStorageWrite; +use orders_accounting::{OrdersAccountingStorageWrite, OrdersAccountingUndo}; use pos_accounting::{ DelegationData, DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingStorageWrite, PoSAccountingUndo, PoolData, @@ -148,6 +148,20 @@ impl<'st, B: storage::Backend> BlockchainStorageWrite for StoreTxRw<'st, B> { self.del::(id) } + #[log_error] + fn set_orders_accounting_undo_data( + &mut self, + id: Id, + undo: &accounting::BlockUndo, + ) -> crate::Result<()> { + todo!() + } + + #[log_error] + fn del_orders_accounting_undo_data(&mut self, id: Id) -> crate::Result<()> { + todo!() + } + #[log_error] fn set_pos_accounting_undo_data( &mut self, diff --git a/chainstate/storage/src/lib.rs b/chainstate/storage/src/lib.rs index d12ab11c2b..29d35a7852 100644 --- a/chainstate/storage/src/lib.rs +++ b/chainstate/storage/src/lib.rs @@ -34,7 +34,9 @@ use common::{ }, primitives::{BlockHeight, Id}, }; -use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingStorageWrite}; +use orders_accounting::{ + OrdersAccountingStorageRead, OrdersAccountingStorageWrite, OrdersAccountingUndo, +}; use pos_accounting::{ DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingStorageRead, PoSAccountingStorageWrite, PoSAccountingUndo, @@ -125,6 +127,12 @@ pub trait BlockchainStorageRead: id: Id, ) -> crate::Result>>; + /// Get tokens accounting undo for specific block + fn get_orders_accounting_undo( + &self, + id: Id, + ) -> crate::Result>>; + /// Get accounting undo for specific block fn get_pos_accounting_undo( &self, @@ -229,6 +237,16 @@ pub trait BlockchainStorageWrite: /// Remove tokens accounting undo data for specific block fn del_tokens_accounting_undo_data(&mut self, id: Id) -> Result<()>; + /// Set orders accounting undo data for specific block + fn set_orders_accounting_undo_data( + &mut self, + id: Id, + undo: &accounting::BlockUndo, + ) -> Result<()>; + + /// Remove orders accounting undo data for specific block + fn del_orders_accounting_undo_data(&mut self, id: Id) -> Result<()>; + /// Set accounting block undo data for specific block fn set_pos_accounting_undo_data( &mut self, diff --git a/chainstate/storage/src/mock/mock_impl.rs b/chainstate/storage/src/mock/mock_impl.rs index 5846e98ceb..4e7bfaa011 100644 --- a/chainstate/storage/src/mock/mock_impl.rs +++ b/chainstate/storage/src/mock/mock_impl.rs @@ -29,7 +29,9 @@ use common::{ }, primitives::{Amount, BlockHeight, Id}, }; -use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingStorageWrite}; +use orders_accounting::{ + OrdersAccountingStorageRead, OrdersAccountingStorageWrite, OrdersAccountingUndo, +}; use pos_accounting::{ DelegationData, DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingUndo, PoolData, }; @@ -78,6 +80,11 @@ mockall::mock! { id: Id, ) -> crate::Result>>; + fn get_orders_accounting_undo( + &self, + id: Id, + ) -> crate::Result>>; + fn get_block_tree_by_height( &self, start_from: BlockHeight, @@ -204,6 +211,13 @@ mockall::mock! { ) -> crate::Result<()>; fn del_tokens_accounting_undo_data(&mut self, id: Id) -> crate::Result<()>; + fn set_orders_accounting_undo_data( + &mut self, + id: Id, + undo: &accounting::BlockUndo, + ) -> crate::Result<()>; + fn del_orders_accounting_undo_data(&mut self, id: Id) -> crate::Result<()>; + fn set_pos_accounting_undo_data(&mut self, id: Id, undo: &accounting::BlockUndo) -> crate::Result<()>; fn del_pos_accounting_undo_data(&mut self, id: Id) -> crate::Result<()>; @@ -371,6 +385,8 @@ mockall::mock! { fn get_tokens_accounting_undo(&self, id: Id) -> crate::Result>>; + fn get_orders_accounting_undo(&self, id: Id) -> crate::Result>>; + fn get_pos_accounting_undo(&self, id: Id) -> crate::Result>>; fn get_accounting_epoch_delta( @@ -497,6 +513,8 @@ mockall::mock! { fn get_pos_accounting_undo(&self, id: Id) -> crate::Result>>; + fn get_orders_accounting_undo(&self, id: Id) -> crate::Result>>; + fn get_accounting_epoch_delta( &self, epoch_index: EpochIndex, @@ -616,6 +634,13 @@ mockall::mock! { ) -> crate::Result<()>; fn del_tokens_accounting_undo_data(&mut self, id: Id) -> crate::Result<()>; + fn set_orders_accounting_undo_data( + &mut self, + id: Id, + undo: &accounting::BlockUndo, + ) -> crate::Result<()>; + fn del_orders_accounting_undo_data(&mut self, id: Id) -> crate::Result<()>; + fn set_pos_accounting_undo_data(&mut self, id: Id, undo: &accounting::BlockUndo) -> crate::Result<()>; fn del_pos_accounting_undo_data(&mut self, id: Id) -> crate::Result<()>; diff --git a/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs b/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs index f7b16a43f4..68942b2f15 100644 --- a/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs +++ b/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs @@ -29,7 +29,7 @@ use common::{ }, primitives::{Amount, Id}, }; -use orders_accounting::OrdersAccountingStorageRead; +use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingUndo}; use pos_accounting::{ DelegationData, PoSAccountingDB, PoSAccountingUndo, PoSAccountingView, PoolData, }; @@ -160,6 +160,25 @@ impl TransactionVerifierStorageRef for InMemoryStorageWrapper { TransactionSource::Mempool => Ok(None), } } + + fn get_orders_accounting_undo( + &self, + tx_source: TransactionSource, + ) -> Result>, TransactionVerifierStorageError> + { + match tx_source { + TransactionSource::Chain(id) => { + let undo = self + .storage + .transaction_ro() + .unwrap() + .get_orders_accounting_undo(id)? + .map(CachedBlockUndo::from_block_undo); + Ok(undo) + } + TransactionSource::Mempool => Ok(None), + } + } } impl UtxosStorageRead for InMemoryStorageWrapper { @@ -243,14 +262,14 @@ impl OrdersAccountingStorageRead for InMemoryStorageWrapper { type Error = storage_result::Error; fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.storage.transaction_ro().unwrap().get_order_data(id) } fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.storage.transaction_ro().unwrap().get_ask_balance(id) } fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.storage.transaction_ro().unwrap().get_give_balance(id) } } diff --git a/chainstate/tx-verifier/src/transaction_verifier/error.rs b/chainstate/tx-verifier/src/transaction_verifier/error.rs index ca398887b2..1dc158c2d7 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/error.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/error.rs @@ -117,7 +117,8 @@ pub enum ConnectTransactionError { RewardDistributionError(#[from] reward_distribution::RewardDistributionError), #[error("Check transaction error: {0}")] CheckTransactionError(#[from] CheckTransactionError), - + #[error("Orders accounting error: {0}")] + OrdersAccountingError(#[from] orders_accounting::Error), #[error(transparent)] InputCheck(#[from] InputCheckError), } diff --git a/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs b/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs index 9c7a7fd2ea..1dd5ede17c 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs @@ -36,7 +36,7 @@ use common::{ }; use orders_accounting::{ FlushableOrdersAccountingView, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData, - OrdersAccountingStorageRead, OrdersAccountingView, + OrdersAccountingStorageRead, OrdersAccountingUndo, OrdersAccountingView, }; use pos_accounting::{ DelegationData, DeltaMergeUndo, FlushablePoSAccountingView, PoSAccountingDeltaData, @@ -145,6 +145,19 @@ where None => self.storage.get_account_nonce_count(account), } } + + fn get_orders_accounting_undo( + &self, + tx_source: TransactionSource, + ) -> Result< + Option>, + ::Error, + > { + match self.orders_accounting_block_undo.data().get(&tx_source) { + Some(op) => Ok(op.get().cloned()), + None => self.storage.get_orders_accounting_undo(tx_source), + } + } } impl UtxosStorageRead @@ -295,6 +308,25 @@ where .del_undo_data(tx_source) .map_err(|e| TransactionVerifierStorageError::AccountingBlockUndoError(e).into()) } + + fn set_orders_accounting_undo_data( + &mut self, + tx_source: TransactionSource, + new_undo: &CachedBlockUndo, + ) -> Result<(), ::Error> { + self.orders_accounting_block_undo + .set_undo_data(tx_source, new_undo) + .map_err(|e| TransactionVerifierStorageError::AccountingBlockUndoError(e).into()) + } + + fn del_orders_accounting_undo_data( + &mut self, + tx_source: TransactionSource, + ) -> Result<(), ::Error> { + self.orders_accounting_block_undo + .del_undo_data(tx_source) + .map_err(|e| TransactionVerifierStorageError::AccountingBlockUndoError(e).into()) + } } impl FlushableUtxoView for TransactionVerifier { diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 61d53d6785..2c359c3eb8 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -31,8 +31,8 @@ mod tx_source; use accounting::BlockRewardUndo; use constraints_value_accumulator::AccumulatedFee; use orders_accounting::{ - OrdersAccountingCache, OrdersAccountingDB, OrdersAccountingDeltaData, OrdersAccountingUndo, - OrdersAccountingView, + OrdersAccountingCache, OrdersAccountingDB, OrdersAccountingDeltaData, + OrdersAccountingOperations, OrdersAccountingUndo, OrdersAccountingView, }; use tokens_accounting::{ TokenAccountingUndo, TokensAccountingCache, TokensAccountingDB, TokensAccountingDeltaData, @@ -68,6 +68,7 @@ use chainstate_types::BlockIndex; use common::{ chain::{ block::{timestamp::BlockTimestamp, BlockRewardTransactable, ConsensusData}, + make_order_id, output_value::OutputValue, signature::Signable, signed_transaction::SignedTransaction, @@ -497,6 +498,8 @@ where self.disconnect_tokens_accounting_outputs(tx_source, tx)?; + self.disconnect_orders_accounting_outputs(tx_source, tx)?; + Ok(()) } @@ -732,6 +735,116 @@ where Ok(()) } + fn connect_orders_outputs( + &mut self, + tx_source: &TransactionSourceForConnect, + tx: &Transaction, + ) -> Result<(), ConnectTransactionError> { + let input_undos = tx + .inputs() + .iter() + .filter_map(|input| match input { + TxInput::Utxo(_) | TxInput::Account(_) => None, + TxInput::AccountCommand(nonce, account_op) => match account_op { + AccountCommand::MintTokens(..) + | AccountCommand::UnmintTokens(..) + | AccountCommand::LockTokenSupply(..) + | AccountCommand::FreezeToken(..) + | AccountCommand::UnfreezeToken(..) + | AccountCommand::ChangeTokenAuthority(..) => None, + AccountCommand::WithdrawOrder(order_id) => { + let res = self + .spend_input_from_account(*nonce, account_op.clone().into()) + .and_then(|_| { + self.orders_accounting_cache + .withdraw_order(*order_id) + .map_err(ConnectTransactionError::OrdersAccountingError) + }); + Some(res) + } + AccountCommand::FillOrder(order_id, fill) => { + let res = self + .spend_input_from_account(*nonce, account_op.clone().into()) + .and_then(|_| { + self.orders_accounting_cache + .fill_order(*order_id, fill.clone()) + .map_err(ConnectTransactionError::OrdersAccountingError) + }); + Some(res) + } + }, + }) + .collect::, _>>()?; + + let input_utxo_outpoint = tx.inputs().iter().find_map(|input| input.utxo_outpoint()); + let output_undos = tx + .outputs() + .iter() + .filter_map(|output| match output { + TxOutput::Transfer(..) + | TxOutput::Burn(..) + | TxOutput::CreateStakePool(..) + | TxOutput::ProduceBlockFromStake(..) + | TxOutput::CreateDelegationId(..) + | TxOutput::DelegateStaking(..) + | TxOutput::LockThenTransfer(..) + | TxOutput::IssueNft(..) + | TxOutput::DataDeposit(..) + | TxOutput::IssueFungibleToken(..) => None, + | TxOutput::CreateOrder(order_data) => match input_utxo_outpoint { + Some(input_utxo_outpoint) => { + let order_id = make_order_id(&input_utxo_outpoint); + let result = self + .orders_accounting_cache + .create_order(order_id, order_data.clone()) + .map_err(ConnectTransactionError::OrdersAccountingError); + Some(result) + } + None => todo!(), + }, + }) + .collect::, _>>()?; + + // Store accounting operations undos + if !input_undos.is_empty() || !output_undos.is_empty() { + let tx_undos = input_undos.into_iter().chain(output_undos).collect(); + self.orders_accounting_block_undo.add_tx_undo( + TransactionSource::from(tx_source), + tx.get_id(), + accounting::TxUndo::new(tx_undos), + )?; + } + + Ok(()) + } + + fn disconnect_orders_accounting_outputs( + &mut self, + tx_source: TransactionSource, + tx: &Transaction, + ) -> Result<(), ConnectTransactionError> { + // apply undos to accounting + let block_undo_fetcher = |tx_source: TransactionSource| { + self.storage + .get_orders_accounting_undo(tx_source) + .map_err(|_| ConnectTransactionError::TxVerifierStorage) + }; + let undos = self.orders_accounting_block_undo.take_tx_undo( + &tx_source, + &tx.get_id(), + block_undo_fetcher, + )?; + if let Some(undos) = undos { + undos + .into_inner() + .into_iter() + .rev() + .try_for_each(|undo| self.orders_accounting_cache.undo(undo))?; + } + + Ok(()) + } + pub fn connect_transaction( &mut self, tx_source: &TransactionSourceForConnect, @@ -773,6 +886,8 @@ where self.connect_tokens_outputs(tx_source, tx.transaction())?; + self.connect_orders_outputs(tx_source, tx.transaction())?; + // spend utxos let tx_undo = self .utxo_cache diff --git a/chainstate/tx-verifier/src/transaction_verifier/storage.rs b/chainstate/tx-verifier/src/transaction_verifier/storage.rs index fe5109a65d..ec969a6b39 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/storage.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/storage.rs @@ -23,7 +23,9 @@ use common::{ }, primitives::Id, }; -use orders_accounting::{FlushableOrdersAccountingView, OrdersAccountingStorageRead}; +use orders_accounting::{ + FlushableOrdersAccountingView, OrdersAccountingStorageRead, OrdersAccountingUndo, +}; use pos_accounting::{ FlushablePoSAccountingView, PoSAccountingDeltaData, PoSAccountingUndo, PoSAccountingView, }; @@ -115,7 +117,13 @@ pub trait TransactionVerifierStorageRef: account: AccountType, ) -> Result, ::Error>; - // FIXME: add methods for order undo + fn get_orders_accounting_undo( + &self, + tx_source: TransactionSource, + ) -> Result< + Option>, + ::Error, + >; } pub trait TransactionVerifierStorageMut: @@ -195,6 +203,17 @@ pub trait TransactionVerifierStorageMut: &mut self, tx_source: TransactionSource, ) -> Result<(), ::Error>; + + fn set_orders_accounting_undo_data( + &mut self, + tx_source: TransactionSource, + undo: &CachedBlockUndo, + ) -> Result<(), ::Error>; + + fn del_orders_accounting_undo_data( + &mut self, + tx_source: TransactionSource, + ) -> Result<(), ::Error>; } impl TransactionVerifierStorageRef for T @@ -257,4 +276,14 @@ where ) -> Result, ::Error> { self.deref().get_account_nonce_count(account) } + + fn get_orders_accounting_undo( + &self, + tx_source: TransactionSource, + ) -> Result< + Option>, + ::Error, + > { + self.deref().get_orders_accounting_undo(tx_source) + } } diff --git a/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs b/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs index 2794ac2257..12ef4ad600 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs @@ -34,7 +34,10 @@ use common::{ }, primitives::{Amount, Id}, }; -use orders_accounting::{FlushableOrdersAccountingView, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData, OrdersAccountingStorageRead}; +use orders_accounting::{ + FlushableOrdersAccountingView, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData, + OrdersAccountingStorageRead, OrdersAccountingUndo, +}; use pos_accounting::{ DelegationData, DeltaMergeUndo, FlushablePoSAccountingView, PoSAccountingDeltaData, PoSAccountingUndo, PoSAccountingView, PoolData, @@ -82,6 +85,11 @@ mockall::mock! { &self, account: AccountType, ) -> Result, TransactionVerifierStorageError>; + + fn get_orders_accounting_undo( + &self, + tx_source: TransactionSource, + ) -> Result>, TransactionVerifierStorageError>; } impl TransactionVerifierStorageMut for Store { @@ -147,6 +155,17 @@ mockall::mock! { &mut self, tx_source: TransactionSource, ) -> Result<(), TransactionVerifierStorageError>; + + fn set_orders_accounting_undo_data( + &mut self, + tx_source: TransactionSource, + undo: &CachedBlockUndo, + ) -> Result<(), TransactionVerifierStorageError>; + + fn del_orders_accounting_undo_data( + &mut self, + tx_source: TransactionSource, + ) -> Result<(), TransactionVerifierStorageError>; } impl UtxosStorageRead for Store { diff --git a/common/src/chain/mod.rs b/common/src/chain/mod.rs index f149dd3877..2e4a712917 100644 --- a/common/src/chain/mod.rs +++ b/common/src/chain/mod.rs @@ -35,7 +35,7 @@ pub use coin_unit::CoinUnit; pub use config::ChainConfig; pub use gen_block::{GenBlock, GenBlockId}; pub use genesis::Genesis; -pub use order::{OrderData, OrderId}; +pub use order::{make_order_id, OrderData, OrderId}; pub use pos::{ config::PoSChainConfig, config_builder::PoSChainConfigBuilder, get_initial_randomness, pos_initial_difficulty, DelegationId, PoSConsensusVersion, PoolId, diff --git a/common/src/chain/order.rs b/common/src/chain/order.rs index 720b940d27..9e2132a9df 100644 --- a/common/src/chain/order.rs +++ b/common/src/chain/order.rs @@ -16,13 +16,13 @@ use crate::{ address::{hexified::HexifiedAddress, traits::Addressable, AddressError}, chain::ChainConfig, - primitives::{Id, H256}, + primitives::{id::hash_encoded, Id, H256}, }; use randomness::{CryptoRng, Rng}; use serialization::{Decode, DecodeAll, Encode}; use typename::TypeName; -use super::{output_value::OutputValue, Destination}; +use super::{output_value::OutputValue, Destination, UtxoOutPoint}; #[derive(Eq, PartialEq, TypeName)] pub enum Order {} @@ -75,6 +75,10 @@ impl<'de> serde::Deserialize<'de> for OrderId { } } +pub fn make_order_id(input0_outpoint: &UtxoOutPoint) -> OrderId { + OrderId::new(hash_encoded(input0_outpoint)) +} + #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] pub struct OrderData { authority: Destination, diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index e2713e1cba..da24ab81db 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -130,6 +130,7 @@ impl MempoolBanScore for ConnectTransactionError { ConnectTransactionError::RewardDistributionError(err) => err.mempool_ban_score(), ConnectTransactionError::CheckTransactionError(err) => err.mempool_ban_score(), ConnectTransactionError::InputCheck(err) => err.mempool_ban_score(), + ConnectTransactionError::OrdersAccountingError(err) => err.mempool_ban_score(), // Transaction definitely invalid, ban peer ConnectTransactionError::RewardAdditionError(_) => 100, @@ -483,3 +484,34 @@ impl MempoolBanScore for CheckTransactionError { } } } + +impl MempoolBanScore for orders_accounting::Error { + fn mempool_ban_score(&self) -> u32 { + use orders_accounting::Error; + + match self { + Error::StorageError(_) => todo!(), + Error::AccountingError(_) => todo!(), + Error::OrderAlreadyExists(_) => todo!(), + Error::OrderDataNotFound(_) => todo!(), + Error::OrderAskBalanceNotFound(_) => todo!(), + Error::OrderGiveBalanceNotFound(_) => todo!(), + Error::InvariantOrderDataNotFoundForUndo(_) => todo!(), + Error::InvariantOrderAskBalanceNotFoundForUndo(_) => todo!(), + Error::InvariantOrderAskBalanceChangedForUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceChangedForUndo(_) => todo!(), + Error::InvariantOrderDataExistForWithdrawUndo(_) => todo!(), + Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => todo!(), + Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => todo!(), + Error::FillOrderChangeLeft(_) => todo!(), + Error::CurrencyMismatch => todo!(), + Error::OrderOverflow(_) => todo!(), + Error::AttemptedWithdrawNonexistingOrderData(_) => todo!(), + Error::AttemptedWithdrawNonexistingAskBalance(_) => todo!(), + Error::AttemptedWithdrawNonexistingGiveBalance(_) => todo!(), + Error::ViewFail => todo!(), + Error::StorageWrite => todo!(), + } + } +} diff --git a/mempool/src/pool/orphans/detect.rs b/mempool/src/pool/orphans/detect.rs index 4ea39725fa..fa3001352f 100644 --- a/mempool/src/pool/orphans/detect.rs +++ b/mempool/src/pool/orphans/detect.rs @@ -76,6 +76,7 @@ impl OrphanType { | CTE::ConstrainedValueAccumulatorError(_, _) | CTE::RewardDistributionError(_) | CTE::CheckTransactionError(_) + | CTE::OrdersAccountingError(_) | CTE::IOPolicyError(_, _) => Err(err), } } diff --git a/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs b/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs index 8cd86f022f..5971ab87cd 100644 --- a/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs +++ b/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs @@ -32,7 +32,7 @@ use common::{ }, primitives::{Amount, Id}, }; -use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingView}; +use orders_accounting::{OrdersAccountingStorageRead, OrdersAccountingUndo, OrdersAccountingView}; use pos_accounting::{DelegationData, PoSAccountingUndo, PoSAccountingView, PoolData}; use subsystem::blocking::BlockingHandle; use tokens_accounting::{TokenAccountingUndo, TokensAccountingStorageRead, TokensAccountingView}; @@ -238,6 +238,13 @@ impl TransactionVerifierStorageRef for ChainstateHandle { ) -> Result>, Error> { Ok(None) } + + fn get_orders_accounting_undo( + &self, + _source: TransactionSource, + ) -> Result>, Error> { + Ok(None) + } } impl UtxosView for ChainstateHandle { From 815be096a8554f3550ab18a540b49bc9dae23add Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Thu, 2 May 2024 10:59:05 +0300 Subject: [PATCH 12/37] Impl signature getter --- .../scanner-lib/src/blockchain_state/mod.rs | 4 +-- .../scanner-lib/src/sync/tests/simulation.rs | 2 +- api-server/web-server/src/api/json_helpers.rs | 2 +- .../src/constraints_accumulator.rs | 2 +- .../src/tests/orders_constraints.rs | 31 ++++++++++++++++--- chainstate/src/detail/ban_score.rs | 2 ++ chainstate/src/detail/error_classification.rs | 4 ++- chainstate/src/rpc/types/account.rs | 2 +- .../test-framework/src/block_builder.rs | 8 +++-- .../test-framework/src/pos_block_builder.rs | 8 +++-- .../src/signature_destination_getter.rs | 25 +++++++++++++-- .../src/transaction_verifier/error.rs | 6 +++- .../src/transaction_verifier/mod.rs | 5 ++- common/src/chain/tokens/tokens_utils.rs | 2 +- .../src/chain/transaction/account_outpoint.rs | 4 +-- common/src/chain/transaction/output/mod.rs | 2 +- mempool/src/error/ban_score.rs | 2 ++ mempool/src/pool/entry.rs | 2 +- mintscript/src/translate.rs | 4 +-- wallet/src/account/mod.rs | 6 ++-- wallet/src/account/output_cache/mod.rs | 14 ++++----- 21 files changed, 97 insertions(+), 40 deletions(-) diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 3a9aa162ae..70cc489b54 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -618,7 +618,7 @@ async fn calculate_fees( | AccountCommand::LockTokenSupply(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) => Some(*token_id), AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, }) .collect(); @@ -1108,7 +1108,7 @@ async fn update_tables_from_transaction_inputs( .await; } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, TxInput::Account(outpoint) => { match outpoint.account() { diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index d8eae0c70b..0a13f16a3b 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -393,7 +393,7 @@ async fn simulation( burn_coins(&mut statistics, token_change_authority_fee); } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, }); } diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index 90772a4268..5f65f0cb7f 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -335,7 +335,7 @@ pub fn tx_input_to_json(inp: &TxInput, chain_config: &ChainConfig) -> serde_json }) } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, } } diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index c5b093e1b1..786e1db608 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -293,7 +293,7 @@ impl ConstrainedValueAccumulator { insert_or_increase(&mut self.unconstrained_value, give_currency, give_balance)?; Ok((CoinOrTokenId::Coin, Amount::ZERO)) } - AccountCommand::FillOrder(order_id, fill_value) => { + AccountCommand::FillOrder(order_id, fill_value, _) => { let filled_amount = orders_accounting::calculate_fill_order( &orders_accounting_view, *order_id, diff --git a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs index 01176e866b..f1a0d2453f 100644 --- a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs +++ b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs @@ -284,6 +284,7 @@ fn fill_order_constraints(#[case] seed: Seed) { AccountCommand::FillOrder( order_id, OutputValue::TokenV1(token_id, (ask_amount + Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, ), ), ]; @@ -319,7 +320,11 @@ fn fill_order_constraints(#[case] seed: Seed) { )), TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::FillOrder(order_id, OutputValue::Coin(ask_amount)), + AccountCommand::FillOrder( + order_id, + OutputValue::Coin(ask_amount), + Destination::AnyoneCanSpend, + ), ), ]; let input_utxos = vec![ @@ -354,7 +359,11 @@ fn fill_order_constraints(#[case] seed: Seed) { )), TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::FillOrder(order_id, OutputValue::TokenV1(token_id, ask_amount)), + AccountCommand::FillOrder( + order_id, + OutputValue::TokenV1(token_id, ask_amount), + Destination::AnyoneCanSpend, + ), ), ]; let input_utxos = vec![ @@ -401,7 +410,11 @@ fn fill_order_constraints(#[case] seed: Seed) { )), TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::FillOrder(order_id, OutputValue::TokenV1(token_id, ask_amount)), + AccountCommand::FillOrder( + order_id, + OutputValue::TokenV1(token_id, ask_amount), + Destination::AnyoneCanSpend, + ), ), ]; let input_utxos = vec![ @@ -453,7 +466,11 @@ fn fill_order_constraints(#[case] seed: Seed) { )), TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::FillOrder(order_id, OutputValue::TokenV1(token_id, ask_amount)), + AccountCommand::FillOrder( + order_id, + OutputValue::TokenV1(token_id, ask_amount), + Destination::AnyoneCanSpend, + ), ), ]; let input_utxos = vec![ @@ -503,7 +520,11 @@ fn fill_order_constraints(#[case] seed: Seed) { )), TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::FillOrder(order_id, OutputValue::TokenV1(token_id, ask_amount)), + AccountCommand::FillOrder( + order_id, + OutputValue::TokenV1(token_id, ask_amount), + Destination::AnyoneCanSpend, + ), ), ]; let input_utxos = vec![ diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 29694b36b2..3eb8b91193 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -232,6 +232,8 @@ impl BanScore for SignatureDestinationGetterError { SignatureDestinationGetterError::UtxoViewError(_) => 100, SignatureDestinationGetterError::TokenDataNotFound(_) => 100, SignatureDestinationGetterError::TokensAccountingViewError(_) => 100, + SignatureDestinationGetterError::OrdersAccountingViewError(_) => 100, + SignatureDestinationGetterError::OrderDataNotFound(_) => 0, } } } diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index 3fe4f9edfd..a76dc71abb 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -540,13 +540,15 @@ impl BlockProcessingErrorClassification for SignatureDestinationGetterError { | SignatureDestinationGetterError::PoolDataNotFound(_) | SignatureDestinationGetterError::DelegationDataNotFound(_) | SignatureDestinationGetterError::TokenDataNotFound(_) - | SignatureDestinationGetterError::UtxoOutputNotFound(_) => { + | SignatureDestinationGetterError::UtxoOutputNotFound(_) + | SignatureDestinationGetterError::OrderDataNotFound(_) => { BlockProcessingErrorClass::BadBlock } SignatureDestinationGetterError::UtxoViewError(err) => err.classify(), SignatureDestinationGetterError::PoSAccountingViewError(err) => err.classify(), SignatureDestinationGetterError::TokensAccountingViewError(err) => err.classify(), + SignatureDestinationGetterError::OrdersAccountingViewError(err) => err.classify(), } } } diff --git a/chainstate/src/rpc/types/account.rs b/chainstate/src/rpc/types/account.rs index b93d054e2e..db0bfcea43 100644 --- a/chainstate/src/rpc/types/account.rs +++ b/chainstate/src/rpc/types/account.rs @@ -104,7 +104,7 @@ impl RpcAccountCommand { } } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }; Ok(result) } diff --git a/chainstate/test-framework/src/block_builder.rs b/chainstate/test-framework/src/block_builder.rs index bca00c0916..a9c41c5245 100644 --- a/chainstate/test-framework/src/block_builder.rs +++ b/chainstate/test-framework/src/block_builder.rs @@ -37,6 +37,7 @@ use common::{ }; use crypto::key::PrivateKey; use itertools::Itertools; +use orders_accounting::{InMemoryOrdersAccounting, OrdersAccountingDB}; use pos_accounting::{InMemoryPoSAccounting, PoSAccountingDB}; use randomness::{CryptoRng, Rng}; use serialization::Encode; @@ -165,8 +166,11 @@ impl<'f> BlockBuilder<'f> { // spending destinations could change let tokens_db = TokensAccountingDB::new(&self.tokens_accounting_store); let pos_db = PoSAccountingDB::new(&self.pos_accounting_store); - let destination_getter = - SignatureDestinationGetter::new_for_transaction(&tokens_db, &pos_db, &utxo_set); + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let destination_getter = SignatureDestinationGetter::new_for_transaction( + &tokens_db, &pos_db, &orders_db, &utxo_set, + ); let witnesses = sign_witnesses( rng, &self.framework.key_manager, diff --git a/chainstate/test-framework/src/pos_block_builder.rs b/chainstate/test-framework/src/pos_block_builder.rs index 80fd56b762..eca35a965f 100644 --- a/chainstate/test-framework/src/pos_block_builder.rs +++ b/chainstate/test-framework/src/pos_block_builder.rs @@ -44,6 +44,7 @@ use crypto::{ key::{PrivateKey, PublicKey}, vrf::VRFPrivateKey, }; +use orders_accounting::{InMemoryOrdersAccounting, OrdersAccountingDB}; use pos_accounting::{InMemoryPoSAccounting, PoSAccountingDB}; use randomness::{seq::IteratorRandom, CryptoRng, Rng}; use serialization::Encode; @@ -405,8 +406,11 @@ impl<'f> PoSBlockBuilder<'f> { // spending destinations could change let tokens_db = TokensAccountingDB::new(&self.tokens_accounting_store); let pos_db = PoSAccountingDB::new(&self.pos_accounting_store); - let destination_getter = - SignatureDestinationGetter::new_for_transaction(&tokens_db, &pos_db, &utxo_set); + let orders_store = InMemoryOrdersAccounting::new(); + let orders_db = OrdersAccountingDB::new(&orders_store); + let destination_getter = SignatureDestinationGetter::new_for_transaction( + &tokens_db, &pos_db, &orders_db, &utxo_set, + ); let witnesses = sign_witnesses( rng, &self.framework.key_manager, diff --git a/chainstate/test-framework/src/signature_destination_getter.rs b/chainstate/test-framework/src/signature_destination_getter.rs index 414fe63cd3..6337fd120d 100644 --- a/chainstate/test-framework/src/signature_destination_getter.rs +++ b/chainstate/test-framework/src/signature_destination_getter.rs @@ -14,6 +14,7 @@ // limitations under the License. use common::chain::{AccountCommand, AccountSpending, Destination, TxInput, TxOutput}; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use tokens_accounting::TokensAccountingView; use tx_verifier::error::SignatureDestinationGetterError; @@ -43,9 +44,15 @@ pub struct SignatureDestinationGetter<'a> { } impl<'a> SignatureDestinationGetter<'a> { - pub fn new_for_transaction( + pub fn new_for_transaction< + T: TokensAccountingView, + P: PoSAccountingView, + U: UtxosView, + O: OrdersAccountingView, + >( tokens_view: &'a T, accounting_view: &'a P, + orders_view: &'a O, utxos_view: &'a U, ) -> Self { let destination_getter = @@ -161,8 +168,20 @@ impl<'a> SignatureDestinationGetter<'a> { }; Ok(destination) } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::WithdrawOrder(order_id) => { + let order_data = orders_view + .get_order_data(order_id) + .map_err(|_| { + SignatureDestinationGetterError::OrdersAccountingViewError( + orders_accounting::Error::ViewFail, + ) + })? + .ok_or(SignatureDestinationGetterError::OrderDataNotFound( + *order_id, + ))?; + Ok(order_data.authority().clone()) + } + AccountCommand::FillOrder(_, _, d) => Ok(d.clone()), }, } }; diff --git a/chainstate/tx-verifier/src/transaction_verifier/error.rs b/chainstate/tx-verifier/src/transaction_verifier/error.rs index 1dc158c2d7..db158c005a 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/error.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/error.rs @@ -18,7 +18,7 @@ use common::{ chain::{ block::{Block, GenBlock}, tokens::TokenId, - AccountNonce, AccountType, DelegationId, OutPointSourceId, PoolId, Transaction, + AccountNonce, AccountType, DelegationId, OrderId, OutPointSourceId, PoolId, Transaction, UtxoOutPoint, }, primitives::{Amount, BlockHeight, CoinOrTokenId, Id}, @@ -151,6 +151,8 @@ pub enum SignatureDestinationGetterError { DelegationDataNotFound(DelegationId), #[error("Token data not found for signature verification {0}")] TokenDataNotFound(TokenId), + #[error("Order data not found for signature verification {0}")] + OrderDataNotFound(OrderId), #[error("Utxo for the outpoint not fount: {0:?}")] UtxoOutputNotFound(UtxoOutPoint), #[error("Error accessing utxo set")] @@ -159,6 +161,8 @@ pub enum SignatureDestinationGetterError { PoSAccountingViewError(#[from] pos_accounting::Error), #[error("During destination getting for signature verification: Tokens accounting error {0}")] TokensAccountingViewError(#[from] tokens_accounting::Error), + #[error("During destination getting for signature verification: Orders accounting error {0}")] + OrdersAccountingViewError(#[from] orders_accounting::Error), } #[derive(Error, Debug, PartialEq, Eq, Clone)] diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 2c359c3eb8..1031bbd75e 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -610,8 +610,7 @@ where }); Some(res) } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => None, }, }) .collect::, _>>()?; @@ -762,7 +761,7 @@ where }); Some(res) } - AccountCommand::FillOrder(order_id, fill) => { + AccountCommand::FillOrder(order_id, fill, _) => { let res = self .spend_input_from_account(*nonce, account_op.clone().into()) .and_then(|_| { diff --git a/common/src/chain/tokens/tokens_utils.rs b/common/src/chain/tokens/tokens_utils.rs index 58f62cf7a5..ae779b76c3 100644 --- a/common/src/chain/tokens/tokens_utils.rs +++ b/common/src/chain/tokens/tokens_utils.rs @@ -57,7 +57,7 @@ pub fn get_token_supply_change_count(inputs: &[TxInput]) -> usize { | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) | AccountCommand::WithdrawOrder(_) - | AccountCommand::FillOrder(_, _) => false, + | AccountCommand::FillOrder(_, _, _) => false, AccountCommand::MintTokens(_, _) | AccountCommand::UnmintTokens(_) | AccountCommand::LockTokenSupply(_) => true, diff --git a/common/src/chain/transaction/account_outpoint.rs b/common/src/chain/transaction/account_outpoint.rs index 765c955304..8e121cd26d 100644 --- a/common/src/chain/transaction/account_outpoint.rs +++ b/common/src/chain/transaction/account_outpoint.rs @@ -53,7 +53,7 @@ impl From for AccountType { | AccountCommand::FreezeToken(id, _) | AccountCommand::UnfreezeToken(id) | AccountCommand::ChangeTokenAuthority(id, _) => AccountType::Token(id), - AccountCommand::WithdrawOrder(id) | AccountCommand::FillOrder(id, _) => { + AccountCommand::WithdrawOrder(id) | AccountCommand::FillOrder(id, _, _) => { AccountType::Order(id) } } @@ -118,7 +118,7 @@ pub enum AccountCommand { #[codec(index = 6)] WithdrawOrder(OrderId), #[codec(index = 7)] - FillOrder(OrderId, OutputValue), + FillOrder(OrderId, OutputValue, Destination), } /// Type of OutPoint that represents spending from an account diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs index 00bf3da096..708a4d1401 100644 --- a/common/src/chain/transaction/output/mod.rs +++ b/common/src/chain/transaction/output/mod.rs @@ -25,7 +25,7 @@ use crate::{ order::OrderData, output_value::OutputValue, tokens::{IsTokenFreezable, NftIssuance, TokenId, TokenIssuance, TokenTotalSupply}, - ChainConfig, DelegationId, OrderId, PoolId, + ChainConfig, DelegationId, PoolId, }, primitives::{Amount, Id}, text_summary::TextSummary, diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index da24ab81db..d0464cc10e 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -250,6 +250,8 @@ impl MempoolBanScore for SignatureDestinationGetterError { SignatureDestinationGetterError::UtxoViewError(_) => 0, SignatureDestinationGetterError::TokenDataNotFound(_) => 0, SignatureDestinationGetterError::TokensAccountingViewError(_) => 100, + SignatureDestinationGetterError::OrdersAccountingViewError(_) => 100, + SignatureDestinationGetterError::OrderDataNotFound(_) => 0, } } } diff --git a/mempool/src/pool/entry.rs b/mempool/src/pool/entry.rs index 52521cb104..e909f5e8ee 100644 --- a/mempool/src/pool/entry.rs +++ b/mempool/src/pool/entry.rs @@ -75,7 +75,7 @@ impl TxDependency { Self::TokenSupplyAccount(TxAccountDependency::new(acct.clone().into(), nonce)) } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), } } diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index ec4ca5bd7f..74311a80d8 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -207,7 +207,7 @@ impl TranslateInput for SignedTransaction { Ok(checksig(dest)) } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, } } @@ -299,7 +299,7 @@ impl TranslateInput for TimelockOnly { | AccountCommand::FreezeToken(_token_id, _) | AccountCommand::UnfreezeToken(_token_id) | AccountCommand::ChangeTokenAuthority(_token_id, _) => Ok(WitnessScript::TRUE), - AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _) => { + AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => { Ok(WitnessScript::TRUE) } }, diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index 3db7f7897f..45e95461d8 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1348,7 +1348,7 @@ impl Account { .map(|data| (None, Some(data.authority.clone()))) .ok_or(WalletError::UnknownTokenId(*token_id)), AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), } } }) @@ -1828,7 +1828,7 @@ impl Account { || self.is_destination_mine_or_watched(address) } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, }); let relevant_outputs = self.mark_outputs_as_seen(db_tx, tx.outputs())?; @@ -2237,7 +2237,7 @@ fn group_preselected_inputs( )?; } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, } } diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index 0c38a60385..8570aa0330 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -672,7 +672,7 @@ impl OutputCache { | AccountCommand::UnfreezeToken(_) => None, AccountCommand::FreezeToken(frozen_token_id, _) => Some(frozen_token_id), AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, }); @@ -732,7 +732,7 @@ impl OutputCache { | AccountCommand::ChangeTokenAuthority(token_id, _) | AccountCommand::UnmintTokens(token_id) => frozen_token_id == token_id, AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, TxInput::Account(_) => false, }) @@ -925,7 +925,7 @@ impl OutputCache { } } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, } } @@ -1027,7 +1027,7 @@ impl OutputCache { } } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, } } @@ -1318,7 +1318,7 @@ impl OutputCache { } } AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, } } @@ -1526,7 +1526,7 @@ fn apply_freeze_mutations_from_tx( | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, } } @@ -1567,7 +1567,7 @@ fn apply_total_supply_mutations_from_tx( | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _) => todo!(), + AccountCommand::FillOrder(_, _, _) => todo!(), }, } } From 3132483a789ae690a17b4741f45d2babd735805f Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Thu, 2 May 2024 11:49:53 +0300 Subject: [PATCH 13/37] Support chainstate storage --- .../src/constraints_accumulator.rs | 12 +++---- chainstate/src/detail/ban_score.rs | 1 + .../chainstateref/tx_verifier_storage.rs | 12 ++++--- chainstate/src/detail/error_classification.rs | 1 + chainstate/src/detail/query.rs | 21 ++++++++++++- .../src/interface/chainstate_interface.rs | 8 +++-- .../interface/chainstate_interface_impl.rs | 31 +++++++++++++++++-- .../chainstate_interface_impl_delegation.rs | 16 ++++++++-- .../src/internal/store_tx/read_impls.rs | 16 +++++----- .../src/internal/store_tx/write_impls.rs | 16 +++++----- chainstate/storage/src/schema.rs | 10 ++++-- .../src/transaction_verifier/error.rs | 2 ++ .../src/transaction_verifier/mod.rs | 4 ++- mempool/src/error/ban_score.rs | 1 + mempool/src/pool/orphans/detect.rs | 1 + .../tx_pool/tx_verifier/chainstate_handle.rs | 18 +++++++---- mocks/src/chainstate.rs | 7 ++++- 17 files changed, 133 insertions(+), 44 deletions(-) diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 786e1db608..0275a52e16 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -71,7 +71,7 @@ impl ConstrainedValueAccumulator { ); let mut accumulator = Self::new(); - let mut total_fee_deducted = BTreeMap::::new(); + let mut total_to_deduct = BTreeMap::::new(); let mut accounts_balances_tracker = AccountsBalancesTracker::new(pos_accounting_view); for (input, input_utxo) in inputs.iter().zip(inputs_utxos.iter()) { @@ -96,23 +96,23 @@ impl ConstrainedValueAccumulator { )?; } TxInput::AccountCommand(_, command) => { - let (id, fee_to_deduct) = accumulator.process_input_account_command( + let (id, to_deduct) = accumulator.process_input_account_command( chain_config, block_height, command, orders_accounting_view, )?; - insert_or_increase(&mut total_fee_deducted, id, fee_to_deduct)?; + insert_or_increase(&mut total_to_deduct, id, to_deduct)?; } } } - for (id, fee) in total_fee_deducted { + for (currency, amount) in total_to_deduct { decrease_or( &mut accumulator.unconstrained_value, - id, - fee, + currency, + amount, Error::AttemptToViolateFeeRequirements, )?; } diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 3eb8b91193..4223f152f3 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -141,6 +141,7 @@ impl BanScore for ConnectTransactionError { ConnectTransactionError::CheckTransactionError(err) => err.ban_score(), ConnectTransactionError::InputCheck(e) => e.ban_score(), ConnectTransactionError::OrdersAccountingError(err) => err.ban_score(), + ConnectTransactionError::AttemptToCreateOrderFromAccounts => 100, } } } diff --git a/chainstate/src/detail/chainstateref/tx_verifier_storage.rs b/chainstate/src/detail/chainstateref/tx_verifier_storage.rs index a115c60a1f..279645576a 100644 --- a/chainstate/src/detail/chainstateref/tx_verifier_storage.rs +++ b/chainstate/src/detail/chainstateref/tx_verifier_storage.rs @@ -34,7 +34,8 @@ use common::{ primitives::{Amount, Id}, }; use orders_accounting::{ - FlushableOrdersAccountingView, OrdersAccountingStorageRead, OrdersAccountingUndo, + FlushableOrdersAccountingView, OrdersAccountingDB, OrdersAccountingStorageRead, + OrdersAccountingUndo, }; use pos_accounting::{ DelegationData, DeltaMergeUndo, FlushablePoSAccountingView, PoSAccountingDB, @@ -560,17 +561,17 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> OrdersAcc #[log_error] fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.db_tx.get_order_data(id) } #[log_error] fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.db_tx.get_ask_balance(id) } #[log_error] fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.db_tx.get_give_balance(id) } } @@ -583,6 +584,7 @@ impl<'a, S: BlockchainStorageWrite, V: TransactionVerificationStrategy> &mut self, delta: orders_accounting::OrdersAccountingDeltaData, ) -> Result { - todo!() + let mut db = OrdersAccountingDB::new(&mut self.db_tx); + db.batch_write_orders_data(delta) } } diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index a76dc71abb..e448faf5e0 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -296,6 +296,7 @@ impl BlockProcessingErrorClassification for ConnectTransactionError { | ConnectTransactionError::NotEnoughPledgeToCreateStakePool(_, _, _) | ConnectTransactionError::AttemptToCreateStakePoolFromAccounts | ConnectTransactionError::AttemptToCreateDelegationFromAccounts + | ConnectTransactionError::AttemptToCreateOrderFromAccounts | ConnectTransactionError::IOPolicyError(_, _) | ConnectTransactionError::TotalFeeRequiredOverflow | ConnectTransactionError::InsufficientCoinsFee(_, _) diff --git a/chainstate/src/detail/query.rs b/chainstate/src/detail/query.rs index c9e9307575..93e369d965 100644 --- a/chainstate/src/detail/query.rs +++ b/chainstate/src/detail/query.rs @@ -24,10 +24,11 @@ use common::{ NftIssuance, RPCFungibleTokenInfo, RPCIsTokenFrozen, RPCNonFungibleTokenInfo, RPCTokenInfo, TokenAuxiliaryData, TokenId, }, - Block, GenBlock, Transaction, TxOutput, + Block, GenBlock, OrderData, OrderId, Transaction, TxOutput, }, primitives::{Amount, BlockDistance, BlockHeight, Id, Idable}, }; +use orders_accounting::OrdersAccountingStorageRead; use tokens_accounting::TokensAccountingStorageRead; use utils::ensure; @@ -402,4 +403,22 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat ) -> Result, PropertyQueryError> { self.chainstate_ref.get_circulating_supply(id).map_err(PropertyQueryError::from) } + + pub fn get_order_data(&self, id: &OrderId) -> Result, PropertyQueryError> { + self.chainstate_ref.get_order_data(id).map_err(PropertyQueryError::from) + } + + pub fn get_order_ask_balance( + &self, + id: &OrderId, + ) -> Result, PropertyQueryError> { + self.chainstate_ref.get_ask_balance(id).map_err(PropertyQueryError::from) + } + + pub fn get_order_give_balance( + &self, + id: &OrderId, + ) -> Result, PropertyQueryError> { + self.chainstate_ref.get_give_balance(id).map_err(PropertyQueryError::from) + } } diff --git a/chainstate/src/interface/chainstate_interface.rs b/chainstate/src/interface/chainstate_interface.rs index abebc8b549..d50fc2bb5d 100644 --- a/chainstate/src/interface/chainstate_interface.rs +++ b/chainstate/src/interface/chainstate_interface.rs @@ -27,8 +27,8 @@ use common::{ GenBlock, }, tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, ChainConfig, DelegationId, PoolId, Transaction, TxInput, - UtxoOutPoint, + AccountNonce, AccountType, ChainConfig, DelegationId, OrderData, OrderId, PoolId, + Transaction, TxInput, UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id}, }; @@ -220,6 +220,10 @@ pub trait ChainstateInterface: Send + Sync { fn get_token_circulating_supply(&self, id: &TokenId) -> Result, ChainstateError>; + fn get_order_data(&self, id: &OrderId) -> Result, ChainstateError>; + fn get_order_ask_balance(&self, id: &OrderId) -> Result, ChainstateError>; + fn get_order_give_balance(&self, id: &OrderId) -> Result, ChainstateError>; + /// Returns the coin amounts of the outpoints spent by a transaction. /// If a utxo for an input was not found or contains tokens the result is `None`. fn get_inputs_outpoints_coin_amount( diff --git a/chainstate/src/interface/chainstate_interface_impl.rs b/chainstate/src/interface/chainstate_interface_impl.rs index c977a950af..7bbdd987a2 100644 --- a/chainstate/src/interface/chainstate_interface_impl.rs +++ b/chainstate/src/interface/chainstate_interface_impl.rs @@ -35,8 +35,8 @@ use common::{ block::{signed_block_header::SignedBlockHeader, Block, BlockReward, GenBlock}, config::ChainConfig, tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, DelegationId, PoolId, Transaction, TxInput, TxOutput, - UtxoOutPoint, + AccountNonce, AccountType, DelegationId, OrderData, OrderId, PoolId, Transaction, TxInput, + TxOutput, UtxoOutPoint, }, primitives::{id::WithId, Amount, BlockHeight, Id, Idable}, }; @@ -767,6 +767,33 @@ where .get_token_circulating_supply(id) .map_err(ChainstateError::from) } + + #[tracing::instrument(skip_all, fields(id = %id))] + fn get_order_data(&self, id: &OrderId) -> Result, ChainstateError> { + self.chainstate + .query() + .map_err(ChainstateError::from)? + .get_order_data(id) + .map_err(ChainstateError::from) + } + + #[tracing::instrument(skip_all, fields(id = %id))] + fn get_order_ask_balance(&self, id: &OrderId) -> Result, ChainstateError> { + self.chainstate + .query() + .map_err(ChainstateError::from)? + .get_order_ask_balance(id) + .map_err(ChainstateError::from) + } + + #[tracing::instrument(skip_all, fields(id = %id))] + fn get_order_give_balance(&self, id: &OrderId) -> Result, ChainstateError> { + self.chainstate + .query() + .map_err(ChainstateError::from)? + .get_order_give_balance(id) + .map_err(ChainstateError::from) + } } // TODO: remove this function. The value of an output cannot be generalized and exposed from ChainstateInterface in such way diff --git a/chainstate/src/interface/chainstate_interface_impl_delegation.rs b/chainstate/src/interface/chainstate_interface_impl_delegation.rs index 0eb6590368..409154137d 100644 --- a/chainstate/src/interface/chainstate_interface_impl_delegation.rs +++ b/chainstate/src/interface/chainstate_interface_impl_delegation.rs @@ -26,8 +26,8 @@ use common::{ block::{signed_block_header::SignedBlockHeader, timestamp::BlockTimestamp, BlockReward}, config::ChainConfig, tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, Block, DelegationId, GenBlock, PoolId, Transaction, TxInput, - UtxoOutPoint, + AccountNonce, AccountType, Block, DelegationId, GenBlock, OrderData, OrderId, PoolId, + Transaction, TxInput, UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id}, }; @@ -409,6 +409,18 @@ where ) -> Result, ChainstateError> { self.deref().get_token_circulating_supply(id) } + + fn get_order_data(&self, id: &OrderId) -> Result, ChainstateError> { + self.deref().get_order_data(id) + } + + fn get_order_ask_balance(&self, id: &OrderId) -> Result, ChainstateError> { + self.deref().get_order_ask_balance(id) + } + + fn get_order_give_balance(&self, id: &OrderId) -> Result, ChainstateError> { + self.deref().get_order_give_balance(id) + } } #[cfg(test)] diff --git a/chainstate/storage/src/internal/store_tx/read_impls.rs b/chainstate/storage/src/internal/store_tx/read_impls.rs index d532b0a8a6..2975e04d97 100644 --- a/chainstate/storage/src/internal/store_tx/read_impls.rs +++ b/chainstate/storage/src/internal/store_tx/read_impls.rs @@ -174,7 +174,7 @@ impl<'st, B: storage::Backend> BlockchainStorageRead for super::StoreTxRo<'st, B &self, id: Id, ) -> crate::Result>> { - todo!() + self.read::(id) } #[log_error] @@ -379,17 +379,17 @@ impl<'st, B: storage::Backend> OrdersAccountingStorageRead for super::StoreTxRo< #[log_error] fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.read::(id) } #[log_error] fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.read::(id) } #[log_error] fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.read::(id) } } @@ -482,7 +482,7 @@ impl<'st, B: storage::Backend> BlockchainStorageRead for super::StoreTxRw<'st, B &self, id: Id, ) -> crate::Result>> { - todo!() + self.read::(id) } #[log_error] @@ -692,16 +692,16 @@ impl<'st, B: storage::Backend> OrdersAccountingStorageRead for super::StoreTxRw< #[log_error] fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.read::(id) } #[log_error] fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.read::(id) } #[log_error] fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + self.read::(id) } } diff --git a/chainstate/storage/src/internal/store_tx/write_impls.rs b/chainstate/storage/src/internal/store_tx/write_impls.rs index 4bb49694f2..73079d820e 100644 --- a/chainstate/storage/src/internal/store_tx/write_impls.rs +++ b/chainstate/storage/src/internal/store_tx/write_impls.rs @@ -154,12 +154,12 @@ impl<'st, B: storage::Backend> BlockchainStorageWrite for StoreTxRw<'st, B> { id: Id, undo: &accounting::BlockUndo, ) -> crate::Result<()> { - todo!() + self.write::(id, undo) } #[log_error] fn del_orders_accounting_undo_data(&mut self, id: Id) -> crate::Result<()> { - todo!() + self.del::(id) } #[log_error] @@ -421,31 +421,31 @@ impl<'st, B: storage::Backend> TokensAccountingStorageWrite for StoreTxRw<'st, B impl<'st, B: storage::Backend> OrdersAccountingStorageWrite for StoreTxRw<'st, B> { #[log_error] fn set_order_data(&mut self, id: &OrderId, data: &OrderData) -> crate::Result<()> { - todo!() + self.write::(id, data) } #[log_error] fn del_order_data(&mut self, id: &OrderId) -> crate::Result<()> { - todo!() + self.del::(id) } #[log_error] fn set_ask_balance(&mut self, id: &OrderId, balance: &Amount) -> crate::Result<()> { - todo!() + self.write::(id, balance) } #[log_error] fn del_ask_balance(&mut self, id: &OrderId) -> crate::Result<()> { - todo!() + self.del::(id) } #[log_error] fn set_give_balance(&mut self, id: &OrderId, balance: &Amount) -> crate::Result<()> { - todo!() + self.write::(id, balance) } #[log_error] fn del_give_balance(&mut self, id: &OrderId) -> crate::Result<()> { - todo!() + self.del::(id) } } diff --git a/chainstate/storage/src/schema.rs b/chainstate/storage/src/schema.rs index 69da5254d7..6ce20515ce 100644 --- a/chainstate/storage/src/schema.rs +++ b/chainstate/storage/src/schema.rs @@ -20,11 +20,12 @@ use common::{ chain::{ config::EpochIndex, tokens::{TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, Block, DelegationId, GenBlock, PoolId, Transaction, - UtxoOutPoint, + AccountNonce, AccountType, Block, DelegationId, GenBlock, OrderData, OrderId, PoolId, + Transaction, UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id}, }; +use orders_accounting::OrdersAccountingUndo; use pos_accounting::{ DelegationData, DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingUndo, PoolData, }; @@ -59,6 +60,11 @@ storage::decl_schema! { pub DBTokensCirculatingSupply: Map, pub DBTokensAccountingBlockUndo: Map, accounting::BlockUndo>, + pub DBOrdersData: Map, + pub DBOrdersAskBalances: Map, + pub DBOrdersGiveBalances: Map, + pub DBOrdersAccountingBlockUndo: Map, accounting::BlockUndo>, + /// Store for accounting BlockUndo pub DBAccountingBlockUndo: Map, accounting::BlockUndo>, /// Store for accounting deltas per epoch diff --git a/chainstate/tx-verifier/src/transaction_verifier/error.rs b/chainstate/tx-verifier/src/transaction_verifier/error.rs index db158c005a..645cba5a8e 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/error.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/error.rs @@ -99,6 +99,8 @@ pub enum ConnectTransactionError { AttemptToCreateStakePoolFromAccounts, #[error("Attempt to create delegation from accounting inputs")] AttemptToCreateDelegationFromAccounts, + #[error("Attempt to create order from accounting inputs")] + AttemptToCreateOrderFromAccounts, #[error("Failed to increment account nonce")] FailedToIncrementAccountNonce, #[error("Input output policy error: `{0}` in : `{1:?}`")] diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 1031bbd75e..c2ebd1cc87 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -799,7 +799,9 @@ where .map_err(ConnectTransactionError::OrdersAccountingError); Some(result) } - None => todo!(), + None => Some(Err( + ConnectTransactionError::AttemptToCreateOrderFromAccounts, + )), }, }) .collect::, _>>()?; diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index d0464cc10e..f76aa3814c 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -141,6 +141,7 @@ impl MempoolBanScore for ConnectTransactionError { ConnectTransactionError::NotEnoughPledgeToCreateStakePool(_, _, _) => 100, ConnectTransactionError::AttemptToCreateStakePoolFromAccounts => 100, ConnectTransactionError::AttemptToCreateDelegationFromAccounts => 100, + ConnectTransactionError::AttemptToCreateOrderFromAccounts => 100, ConnectTransactionError::TotalFeeRequiredOverflow => 100, ConnectTransactionError::InsufficientCoinsFee(_, _) => 100, diff --git a/mempool/src/pool/orphans/detect.rs b/mempool/src/pool/orphans/detect.rs index fa3001352f..b426ba3e27 100644 --- a/mempool/src/pool/orphans/detect.rs +++ b/mempool/src/pool/orphans/detect.rs @@ -77,6 +77,7 @@ impl OrphanType { | CTE::RewardDistributionError(_) | CTE::CheckTransactionError(_) | CTE::OrdersAccountingError(_) + | CTE::AttemptToCreateOrderFromAccounts | CTE::IOPolicyError(_, _) => Err(err), } } diff --git a/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs b/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs index 5971ab87cd..729801cb90 100644 --- a/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs +++ b/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs @@ -306,15 +306,18 @@ impl OrdersAccountingView for ChainstateHandle { type Error = Error; fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + let id = *id; + self.call(move |c| c.get_order_data(&id)) } fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + let id = *id; + self.call(move |c| c.get_order_ask_balance(&id)) } fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + let id = *id; + self.call(move |c| c.get_order_give_balance(&id)) } } @@ -322,14 +325,17 @@ impl OrdersAccountingStorageRead for ChainstateHandle { type Error = Error; fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + let id = *id; + self.call(move |c| c.get_order_data(&id)) } fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + let id = *id; + self.call(move |c| c.get_order_ask_balance(&id)) } fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error> { - todo!() + let id = *id; + self.call(move |c| c.get_order_give_balance(&id)) } } diff --git a/mocks/src/chainstate.rs b/mocks/src/chainstate.rs index 44f47a5e61..ecb7aa654b 100644 --- a/mocks/src/chainstate.rs +++ b/mocks/src/chainstate.rs @@ -26,7 +26,8 @@ use common::{ GenBlock, }, tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, ChainConfig, DelegationId, PoolId, TxInput, UtxoOutPoint, + AccountNonce, AccountType, ChainConfig, DelegationId, OrderData, OrderId, PoolId, TxInput, + UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id}, }; @@ -198,6 +199,10 @@ mockall::mock! { &self, account: AccountType, ) -> Result, ChainstateError>; + + fn get_order_data(&self, id: &OrderId) -> Result, ChainstateError>; + fn get_order_ask_balance(&self, id: &OrderId) -> Result, ChainstateError>; + fn get_order_give_balance(&self, id: &OrderId) -> Result, ChainstateError>; } } From 1b3a33abb0b90768bf933c7099d50fd56662bc4f Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Thu, 2 May 2024 20:19:10 +0300 Subject: [PATCH 14/37] More chainstate tests --- chainstate/src/detail/ban_score.rs | 2 + chainstate/src/detail/error.rs | 2 + chainstate/src/detail/error_classification.rs | 51 +- chainstate/test-framework/src/utils.rs | 4 +- .../src/tests/fungible_tokens_v1.rs | 102 +-- .../test-suite/src/tests/helpers/mod.rs | 114 ++- chainstate/test-suite/src/tests/mod.rs | 1 + .../test-suite/src/tests/orders_tests.rs | 670 ++++++++++++++++++ .../accounting_undo_cache/tests.rs | 25 + .../src/transaction_verifier/flush.rs | 15 + .../src/transaction_verifier/mod.rs | 6 +- .../src/transaction_verifier/storage.rs | 2 + .../tests/hierarchy_write.rs | 73 ++ .../utxos_undo_cache/tests.rs | 25 + .../src/chain/upgrades/chainstate_upgrade.rs | 2 + mempool/src/error/ban_score.rs | 1 + .../tx_pool/tx_verifier/chainstate_handle.rs | 6 + orders-accounting/src/cache.rs | 4 +- orders-accounting/src/data.rs | 10 + orders-accounting/src/lib.rs | 2 +- orders-accounting/src/tests/operations.rs | 418 ++++++++++- pos-accounting/src/lib.rs | 2 +- 22 files changed, 1396 insertions(+), 141 deletions(-) create mode 100644 chainstate/test-suite/src/tests/orders_tests.rs diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 4223f152f3..fc612a8973 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -84,6 +84,7 @@ impl BanScore for BlockError { BlockError::UnexpectedHeightRange(_, _) => 0, BlockError::TokensAccountingError(err) => err.ban_score(), + BlockError::OrdersAccountingError(err) => err.ban_score(), } } } @@ -252,6 +253,7 @@ impl BanScore for TransactionVerifierStorageError { TransactionVerifierStorageError::PoSAccountingError(err) => err.ban_score(), TransactionVerifierStorageError::AccountingBlockUndoError(_) => 100, TransactionVerifierStorageError::TokensAccountingError(err) => err.ban_score(), + TransactionVerifierStorageError::OrdersAccountingError(err) => err.ban_score(), } } } diff --git a/chainstate/src/detail/error.rs b/chainstate/src/detail/error.rs index 0ff1d466da..b2d5a5e5d7 100644 --- a/chainstate/src/detail/error.rs +++ b/chainstate/src/detail/error.rs @@ -74,6 +74,8 @@ pub enum BlockError { TokensAccountingError(#[from] tokens_accounting::Error), #[error("In-memory reorg failed: {0}")] InMemoryReorgFailed(#[from] InMemoryReorgError), + #[error("Orders accounting error: {0}")] + OrdersAccountingError(#[from] orders_accounting::Error), #[error("Failed to obtain best block id: {0}")] BestBlockIdQueryError(PropertyQueryError), diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index e448faf5e0..b05fe20c52 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -130,6 +130,7 @@ impl BlockProcessingErrorClassification for BlockError { BlockError::BestChainCandidatesAccessorError(err) => err.classify(), BlockError::TokensAccountingError(err) => err.classify(), + BlockError::OrdersAccountingError(err) => err.classify(), BlockError::StorageError(err) => err.classify(), BlockError::OrphanCheckFailed(err) => err.classify(), BlockError::CheckBlockFailed(err) => err.classify(), @@ -491,6 +492,7 @@ impl BlockProcessingErrorClassification for TransactionVerifierStorageError { TransactionVerifierStorageError::PoSAccountingError(err) => err.classify(), TransactionVerifierStorageError::AccountingBlockUndoError(err) => err.classify(), TransactionVerifierStorageError::TokensAccountingError(err) => err.classify(), + TransactionVerifierStorageError::OrdersAccountingError(err) => err.classify(), } } } @@ -878,7 +880,7 @@ impl BlockProcessingErrorClassification for constraints_value_accumulator::Error | Error::NegativeAccountBalance(_) => BlockProcessingErrorClass::BadBlock, Error::PoSAccountingError(err) => err.classify(), - Error::OrdersAccountingError(_) => todo!(), + Error::OrdersAccountingError(err) => err.classify(), } } } @@ -887,28 +889,31 @@ impl BlockProcessingErrorClassification for orders_accounting::Error { fn classify(&self) -> BlockProcessingErrorClass { use orders_accounting::Error; match self { - Error::StorageError(_) => todo!(), - Error::AccountingError(_) => todo!(), - Error::OrderAlreadyExists(_) => todo!(), - Error::OrderDataNotFound(_) => todo!(), - Error::OrderAskBalanceNotFound(_) => todo!(), - Error::OrderGiveBalanceNotFound(_) => todo!(), - Error::InvariantOrderDataNotFoundForUndo(_) => todo!(), - Error::InvariantOrderAskBalanceNotFoundForUndo(_) => todo!(), - Error::InvariantOrderAskBalanceChangedForUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceChangedForUndo(_) => todo!(), - Error::InvariantOrderDataExistForWithdrawUndo(_) => todo!(), - Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => todo!(), - Error::FillOrderChangeLeft(_) => todo!(), - Error::CurrencyMismatch => todo!(), - Error::OrderOverflow(_) => todo!(), - Error::AttemptedWithdrawNonexistingOrderData(_) => todo!(), - Error::AttemptedWithdrawNonexistingAskBalance(_) => todo!(), - Error::AttemptedWithdrawNonexistingGiveBalance(_) => todo!(), - Error::ViewFail => todo!(), - Error::StorageWrite => todo!(), + Error::ViewFail | Error::StorageWrite => BlockProcessingErrorClass::General, + + Error::OrderAlreadyExists(_) + | Error::OrderDataNotFound(_) + | Error::OrderAskBalanceNotFound(_) + | Error::OrderGiveBalanceNotFound(_) + | Error::InvariantOrderDataNotFoundForUndo(_) + | Error::InvariantOrderAskBalanceNotFoundForUndo(_) + | Error::InvariantOrderAskBalanceChangedForUndo(_) + | Error::InvariantOrderGiveBalanceNotFoundForUndo(_) + | Error::InvariantOrderGiveBalanceChangedForUndo(_) + | Error::InvariantOrderDataExistForWithdrawUndo(_) + | Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) + | Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) + | Error::FillOrderChangeLeft(_) + | Error::CurrencyMismatch + | Error::OrderOverflow(_) + | Error::AttemptedWithdrawNonexistingOrderData(_) + | Error::AttemptedWithdrawNonexistingAskBalance(_) + | Error::AttemptedWithdrawNonexistingGiveBalance(_) => { + BlockProcessingErrorClass::BadBlock + } + + Error::StorageError(err) => err.classify(), + Error::AccountingError(err) => err.classify(), } } } diff --git a/chainstate/test-framework/src/utils.rs b/chainstate/test-framework/src/utils.rs index 3164b972f5..13aac4dd80 100644 --- a/chainstate/test-framework/src/utils.rs +++ b/chainstate/test-framework/src/utils.rs @@ -68,11 +68,11 @@ pub fn get_output_value(output: &TxOutput) -> Option { | TxOutput::CreateDelegationId(_, _) | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) - | TxOutput::DataDeposit(_) => None, + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => None, TxOutput::IssueNft(token_id, _, _) => { Some(OutputValue::TokenV1(*token_id, Amount::from_atoms(1))) } - TxOutput::CreateOrder(_) => todo!(), } } diff --git a/chainstate/test-suite/src/tests/fungible_tokens_v1.rs b/chainstate/test-suite/src/tests/fungible_tokens_v1.rs index cc8799e4ef..9777f32ccc 100644 --- a/chainstate/test-suite/src/tests/fungible_tokens_v1.rs +++ b/chainstate/test-suite/src/tests/fungible_tokens_v1.rs @@ -53,6 +53,8 @@ use tx_verifier::{ CheckTransactionError, }; +use crate::tests::helpers::{issue_token_from_block, mint_tokens_in_block}; + fn make_issuance( rng: &mut impl Rng, supply: TokenTotalSupply, @@ -68,43 +70,6 @@ fn make_issuance( }) } -fn issue_token_from_block( - rng: &mut (impl Rng + CryptoRng), - tf: &mut TestFramework, - parent_block_id: Id, - utxo_to_pay_fee: UtxoOutPoint, - issuance: TokenIssuance, -) -> (TokenId, Id, UtxoOutPoint) { - let token_issuance_fee = tf.chainstate.get_chain_config().fungible_token_issuance_fee(); - - let fee_utxo_coins = chainstate_test_framework::get_output_value( - tf.chainstate.utxo(&utxo_to_pay_fee).unwrap().unwrap().output(), - ) - .unwrap() - .coin_amount() - .unwrap(); - - let tx = TransactionBuilder::new() - .add_input(utxo_to_pay_fee.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::Transfer( - OutputValue::Coin((fee_utxo_coins - token_issuance_fee).unwrap()), - Destination::AnyoneCanSpend, - )) - .add_output(TxOutput::IssueFungibleToken(Box::new(issuance.clone()))) - .build(); - let token_id = make_token_id(tx.transaction().inputs()).unwrap(); - let tx_id = tx.transaction().get_id(); - let block = tf - .make_block_builder() - .add_transaction(tx) - .with_parent(parent_block_id) - .build(rng); - let block_id = block.get_id(); - tf.process_block(block, BlockSource::Local).unwrap(); - - (token_id, block_id, UtxoOutPoint::new(tx_id.into(), 0)) -} - // Returns created token id and outpoint with change fn issue_token_from_genesis( rng: &mut (impl Rng + CryptoRng), @@ -123,69 +88,6 @@ fn issue_token_from_genesis( ) } -fn mint_tokens_in_block( - rng: &mut (impl Rng + CryptoRng), - tf: &mut TestFramework, - parent_block_id: Id, - utxo_to_pay_fee: UtxoOutPoint, - token_id: TokenId, - amount_to_mint: Amount, - produce_change: bool, -) -> (Id, Id) { - let token_supply_change_fee = - tf.chainstate.get_chain_config().token_supply_change_fee(BlockHeight::zero()); - - let nonce = BlockchainStorageRead::get_account_nonce_count( - &tf.storage.transaction_ro().unwrap(), - AccountType::Token(token_id), - ) - .unwrap() - .map_or(AccountNonce::new(0), |n| n.increment().unwrap()); - - let tx_builder = TransactionBuilder::new() - .add_input( - TxInput::from_command(nonce, AccountCommand::MintTokens(token_id, amount_to_mint)), - InputWitness::NoSignature(None), - ) - .add_input( - utxo_to_pay_fee.clone().into(), - InputWitness::NoSignature(None), - ) - .add_output(TxOutput::Transfer( - OutputValue::TokenV1(token_id, amount_to_mint), - Destination::AnyoneCanSpend, - )); - - let tx_builder = if produce_change { - let fee_utxo_coins = chainstate_test_framework::get_output_value( - tf.chainstate.utxo(&utxo_to_pay_fee).unwrap().unwrap().output(), - ) - .unwrap() - .coin_amount() - .unwrap(); - - tx_builder.add_output(TxOutput::Transfer( - OutputValue::Coin((fee_utxo_coins - token_supply_change_fee).unwrap()), - Destination::AnyoneCanSpend, - )) - } else { - tx_builder - }; - - let tx = tx_builder.build(); - let tx_id = tx.transaction().get_id(); - - let block = tf - .make_block_builder() - .add_transaction(tx) - .with_parent(parent_block_id) - .build(rng); - let block_id = block.get_id(); - tf.process_block(block, BlockSource::Local).unwrap(); - - (block_id, tx_id) -} - fn unmint_tokens_in_block( rng: &mut (impl Rng + CryptoRng), tf: &mut TestFramework, diff --git a/chainstate/test-suite/src/tests/helpers/mod.rs b/chainstate/test-suite/src/tests/helpers/mod.rs index 176214192e..8dee793b5a 100644 --- a/chainstate/test-suite/src/tests/helpers/mod.rs +++ b/chainstate/test-suite/src/tests/helpers/mod.rs @@ -13,14 +13,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +use chainstate::BlockSource; +use chainstate_storage::{BlockchainStorageRead, Transactional}; use chainstate_test_framework::{anyonecanspend_address, TestFramework, TransactionBuilder}; use common::{ chain::{ - block::timestamp::BlockTimestamp, output_value::OutputValue, - signature::inputsig::InputWitness, timelock::OutputTimeLock, Destination, Transaction, - TxInput, TxOutput, + block::timestamp::BlockTimestamp, + output_value::OutputValue, + signature::inputsig::InputWitness, + timelock::OutputTimeLock, + tokens::{make_token_id, TokenId, TokenIssuance}, + AccountCommand, AccountNonce, AccountType, Block, Destination, GenBlock, Transaction, + TxInput, TxOutput, UtxoOutPoint, }, - primitives::{Amount, BlockDistance, Id, Idable}, + primitives::{Amount, BlockDistance, BlockHeight, Id, Idable}, }; use crypto::key::{KeyKind, PrivateKey}; use randomness::{CryptoRng, Rng}; @@ -77,3 +83,103 @@ pub fn new_pub_key_destination(rng: &mut (impl Rng + CryptoRng)) -> Destination let (_, pub_key) = PrivateKey::new_from_rng(rng, KeyKind::Secp256k1Schnorr); Destination::PublicKey(pub_key) } + +pub fn issue_token_from_block( + rng: &mut (impl Rng + CryptoRng), + tf: &mut TestFramework, + parent_block_id: Id, + utxo_to_pay_fee: UtxoOutPoint, + issuance: TokenIssuance, +) -> (TokenId, Id, UtxoOutPoint) { + let token_issuance_fee = tf.chainstate.get_chain_config().fungible_token_issuance_fee(); + + let fee_utxo_coins = chainstate_test_framework::get_output_value( + tf.chainstate.utxo(&utxo_to_pay_fee).unwrap().unwrap().output(), + ) + .unwrap() + .coin_amount() + .unwrap(); + + let tx = TransactionBuilder::new() + .add_input(utxo_to_pay_fee.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::Transfer( + OutputValue::Coin((fee_utxo_coins - token_issuance_fee).unwrap()), + Destination::AnyoneCanSpend, + )) + .add_output(TxOutput::IssueFungibleToken(Box::new(issuance.clone()))) + .build(); + let token_id = make_token_id(tx.transaction().inputs()).unwrap(); + let tx_id = tx.transaction().get_id(); + let block = tf + .make_block_builder() + .add_transaction(tx) + .with_parent(parent_block_id) + .build(rng); + let block_id = block.get_id(); + tf.process_block(block, BlockSource::Local).unwrap(); + + (token_id, block_id, UtxoOutPoint::new(tx_id.into(), 0)) +} + +pub fn mint_tokens_in_block( + rng: &mut (impl Rng + CryptoRng), + tf: &mut TestFramework, + parent_block_id: Id, + utxo_to_pay_fee: UtxoOutPoint, + token_id: TokenId, + amount_to_mint: Amount, + produce_change: bool, +) -> (Id, Id) { + let token_supply_change_fee = + tf.chainstate.get_chain_config().token_supply_change_fee(BlockHeight::zero()); + + let nonce = BlockchainStorageRead::get_account_nonce_count( + &tf.storage.transaction_ro().unwrap(), + AccountType::Token(token_id), + ) + .unwrap() + .map_or(AccountNonce::new(0), |n| n.increment().unwrap()); + + let tx_builder = TransactionBuilder::new() + .add_input( + TxInput::from_command(nonce, AccountCommand::MintTokens(token_id, amount_to_mint)), + InputWitness::NoSignature(None), + ) + .add_input( + utxo_to_pay_fee.clone().into(), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, amount_to_mint), + Destination::AnyoneCanSpend, + )); + + let tx_builder = if produce_change { + let fee_utxo_coins = chainstate_test_framework::get_output_value( + tf.chainstate.utxo(&utxo_to_pay_fee).unwrap().unwrap().output(), + ) + .unwrap() + .coin_amount() + .unwrap(); + + tx_builder.add_output(TxOutput::Transfer( + OutputValue::Coin((fee_utxo_coins - token_supply_change_fee).unwrap()), + Destination::AnyoneCanSpend, + )) + } else { + tx_builder + }; + + let tx = tx_builder.build(); + let tx_id = tx.transaction().get_id(); + + let block = tf + .make_block_builder() + .add_transaction(tx) + .with_parent(parent_block_id) + .build(rng); + let block_id = block.get_id(); + tf.process_block(block, BlockSource::Local).unwrap(); + + (block_id, tx_id) +} diff --git a/chainstate/test-suite/src/tests/mod.rs b/chainstate/test-suite/src/tests/mod.rs index c3051369bd..7a871ac641 100644 --- a/chainstate/test-suite/src/tests/mod.rs +++ b/chainstate/test-suite/src/tests/mod.rs @@ -49,6 +49,7 @@ mod nft_burn; mod nft_issuance; mod nft_reorgs; mod nft_transfer; +mod orders_tests; mod output_timelock; mod pos_accounting_reorg; mod pos_maturity_settings; diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs new file mode 100644 index 0000000000..4e8d50f33d --- /dev/null +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -0,0 +1,670 @@ +// Copyright (c) 2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use chainstate::ConnectTransactionError; +use chainstate_storage::Transactional; +use chainstate_test_framework::{TestFramework, TransactionBuilder}; +use common::{ + chain::{ + make_order_id, + output_value::OutputValue, + signature::{ + inputsig::{standard_signature::StandardInputSignature, InputWitness}, + DestinationSigError, + }, + tokens::{TokenId, TokenIssuance}, + AccountCommand, AccountNonce, Destination, OrderData, SignedTransaction, TxInput, TxOutput, + UtxoOutPoint, + }, + primitives::{Amount, Idable}, +}; +use crypto::key::{KeyKind, PrivateKey}; +use orders_accounting::OrdersAccountingDB; +use randomness::{CryptoRng, Rng}; +use rstest::rstest; +use test_utils::{ + nft_utils::random_token_issuance_v1, + random::{make_seedable_rng, Seed}, +}; +use tx_verifier::error::{InputCheckError, ScriptError}; + +use crate::tests::helpers::{issue_token_from_block, mint_tokens_in_block}; + +fn issue_and_mint_token( + rng: &mut (impl Rng + CryptoRng), + tf: &mut TestFramework, +) -> (TokenId, UtxoOutPoint, UtxoOutPoint) { + let genesis_block_id = tf.best_block_id(); + let issuance = TokenIssuance::V1(random_token_issuance_v1( + tf.chain_config(), + Destination::AnyoneCanSpend, + rng, + )); + let (token_id, _, utxo_with_change) = issue_token_from_block( + rng, + tf, + genesis_block_id, + UtxoOutPoint::new(genesis_block_id.into(), 0), + issuance, + ); + + let best_block_id = tf.best_block_id(); + let (_, mint_tx_id) = mint_tokens_in_block( + rng, + tf, + best_block_id, + utxo_with_change, + token_id, + Amount::from_atoms(100_000), + true, + ); + + ( + token_id, + UtxoOutPoint::new(mint_tx_id.into(), 0), + UtxoOutPoint::new(mint_tx_id.into(), 1), + ) +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_order_check_storage(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, _) = issue_and_mint_token(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::CreateOrder(order_data.clone())) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + assert_eq!( + Some(order_data), + tf.chainstate.get_order_data(&order_id).unwrap() + ); + assert_eq!( + Some(ask_amount), + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(give_amount), + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + }); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_two_orders_same_tx(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, _) = issue_and_mint_token(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::CreateOrder(order_data.clone())) + .add_output(TxOutput::CreateOrder(order_data.clone())) + .build(); + let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + + assert_eq!( + result.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::StateUpdateFailed( + chainstate::ConnectTransactionError::OrdersAccountingError( + orders_accounting::Error::OrderAlreadyExists(order_id) + ) + ) + ) + ); + }); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn withdraw_order_check_storage(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, _) = issue_and_mint_token(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::CreateOrder(order_data.clone())) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, give_amount), + Destination::AnyoneCanSpend, + )) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap()); + assert_eq!( + None, + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + None, + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + }); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_order_check_storage(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, coins_outpoint) = issue_and_mint_token(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::CreateOrder(order_data.clone())) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + // Fill the order partially + let fill_value = OutputValue::Coin(Amount::from_atoms( + rng.gen_range(1..ask_amount.into_atoms()), + )); + let filled_amount = { + let db_tx = tf.storage.transaction_ro().unwrap(); + let orders_db = OrdersAccountingDB::new(&db_tx); + orders_accounting::calculate_fill_order(&orders_db, order_id, &fill_value).unwrap() + }; + let left_to_fill = (ask_amount - fill_value.amount()).unwrap(); + + let tx = TransactionBuilder::new() + .add_input(coins_outpoint.into(), InputWitness::NoSignature(None)) + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder(order_id, fill_value, Destination::AnyoneCanSpend), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, filled_amount), + Destination::AnyoneCanSpend, + )) + .add_output(TxOutput::Transfer( + OutputValue::Coin(left_to_fill), + Destination::AnyoneCanSpend, + )) + .build(); + let partial_fill_tx_id = tx.transaction().get_id(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + assert_eq!( + Some(order_data), + tf.chainstate.get_order_data(&order_id).unwrap() + ); + assert_eq!( + Some(left_to_fill), + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some((give_amount - filled_amount).unwrap()), + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + + // Fill the rest of the order + let fill_value = OutputValue::Coin(left_to_fill); + let filled_amount = { + let db_tx = tf.storage.transaction_ro().unwrap(); + let orders_db = OrdersAccountingDB::new(&db_tx); + orders_accounting::calculate_fill_order(&orders_db, order_id, &fill_value).unwrap() + }; + + let tx = TransactionBuilder::new() + .add_input( + UtxoOutPoint::new(partial_fill_tx_id.into(), 1).into(), + InputWitness::NoSignature(None), + ) + .add_input( + TxInput::AccountCommand( + AccountNonce::new(1), + AccountCommand::FillOrder(order_id, fill_value, Destination::AnyoneCanSpend), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, filled_amount), + Destination::AnyoneCanSpend, + )) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap()); + assert_eq!( + None, + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + None, + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + }); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn withdraw_order_check_signature(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (order_sk, order_pk) = PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr); + + let (token_id, tokens_outpoint, _) = issue_and_mint_token(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let order_data = OrderData::new( + Destination::PublicKey(order_pk.clone()), + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::CreateOrder(order_data.clone())) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + // try withdraw without signature + { + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, give_amount), + Destination::AnyoneCanSpend, + )) + .build(); + let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + + assert_eq!( + result.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::StateUpdateFailed(ConnectTransactionError::InputCheck( + InputCheckError::new( + 0, + ScriptError::Signature(DestinationSigError::SignatureNotFound) + ) + )) + ) + ) + } + + // try withdraw with wrong signature + { + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, give_amount), + Destination::AnyoneCanSpend, + )) + .build(); + + let inputs_utxos: Vec> = vec![None]; + let inputs_utxos_refs = + inputs_utxos.iter().map(|utxo| utxo.as_ref()).collect::>(); + let (some_sk, some_pk) = PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr); + let account_sig = StandardInputSignature::produce_uniparty_signature_for_input( + &some_sk, + Default::default(), + Destination::PublicKey(some_pk), + &tx, + &inputs_utxos_refs, + 0, + &mut rng, + ) + .unwrap(); + + let tx = SignedTransaction::new( + tx.take_transaction(), + vec![InputWitness::Standard(account_sig)], + ) + .unwrap(); + let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + + assert_eq!( + result.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::StateUpdateFailed(ConnectTransactionError::InputCheck( + InputCheckError::new( + 0, + ScriptError::Signature( + DestinationSigError::SignatureVerificationFailed + ) + ) + )) + ) + ) + } + + // valid case + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, give_amount), + Destination::AnyoneCanSpend, + )) + .build(); + + let inputs_utxos: Vec> = vec![None]; + let inputs_utxos_refs = inputs_utxos.iter().map(|utxo| utxo.as_ref()).collect::>(); + let account_sig = StandardInputSignature::produce_uniparty_signature_for_input( + &order_sk, + Default::default(), + Destination::PublicKey(order_pk), + &tx, + &inputs_utxos_refs, + 0, + &mut rng, + ) + .unwrap(); + + let tx = SignedTransaction::new( + tx.take_transaction(), + vec![InputWitness::Standard(account_sig)], + ) + .unwrap(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + }); +} + +// Create a chain with an order which is filled partially. +// Reorg from a point before the order was created, so that after reorg storage has no information on the order +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn reorg_before_create(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, coins_outpoint) = issue_and_mint_token(&mut rng, &mut tf); + let reorg_common_ancestor = tf.best_block_id(); + + let ask_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::CreateOrder(order_data.clone())) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + // Fill the order partially + let fill_value = OutputValue::Coin(Amount::from_atoms( + rng.gen_range(1..ask_amount.into_atoms()), + )); + let filled_amount = { + let db_tx = tf.storage.transaction_ro().unwrap(); + let orders_db = OrdersAccountingDB::new(&db_tx); + orders_accounting::calculate_fill_order(&orders_db, order_id, &fill_value).unwrap() + }; + let left_to_fill = (ask_amount - fill_value.amount()).unwrap(); + + let tx = TransactionBuilder::new() + .add_input(coins_outpoint.into(), InputWitness::NoSignature(None)) + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder(order_id, fill_value, Destination::AnyoneCanSpend), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, filled_amount), + Destination::AnyoneCanSpend, + )) + .add_output(TxOutput::Transfer( + OutputValue::Coin(left_to_fill), + Destination::AnyoneCanSpend, + )) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + assert_eq!( + Some(order_data), + tf.chainstate.get_order_data(&order_id).unwrap() + ); + assert_eq!( + Some(left_to_fill), + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some((give_amount - filled_amount).unwrap()), + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + + // Create alternative chain and trigger the reorg + let new_best_block = tf.create_chain(&reorg_common_ancestor, 3, &mut rng).unwrap(); + assert_eq!(tf.best_block_id(), new_best_block); + + assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap()); + assert_eq!( + None, + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + None, + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + }); +} + +// Create a chain with an order which is filled partially and then withdrawn. +// Reorg from a point after the order was created, so that after reorg storage has original information on the order +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn reorg_after_create(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, coins_outpoint) = issue_and_mint_token(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::CreateOrder(order_data.clone())) + // transfer output just to be able to spend something in alternative branch + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, Amount::from_atoms(100)), + Destination::AnyoneCanSpend, + )) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + let reorg_common_ancestor = tf.best_block_id(); + + // Fill the order partially + let fill_value = OutputValue::Coin(Amount::from_atoms( + rng.gen_range(1..ask_amount.into_atoms()), + )); + let filled_amount = { + let db_tx = tf.storage.transaction_ro().unwrap(); + let orders_db = OrdersAccountingDB::new(&db_tx); + orders_accounting::calculate_fill_order(&orders_db, order_id, &fill_value).unwrap() + }; + let left_to_fill = (ask_amount - fill_value.amount()).unwrap(); + + tf.make_block_builder() + .add_transaction( + TransactionBuilder::new() + .add_input(coins_outpoint.into(), InputWitness::NoSignature(None)) + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder( + order_id, + fill_value, + Destination::AnyoneCanSpend, + ), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, filled_amount), + Destination::AnyoneCanSpend, + )) + .add_output(TxOutput::Transfer( + OutputValue::Coin(left_to_fill), + Destination::AnyoneCanSpend, + )) + .build(), + ) + .build_and_process(&mut rng) + .unwrap(); + + tf.make_block_builder() + .add_transaction( + TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(1), + AccountCommand::WithdrawOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .build(), + ) + .build_and_process(&mut rng) + .unwrap(); + + assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap()); + assert_eq!( + None, + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + None, + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + + // Create alternative chain and trigger the reorg + let new_best_block = tf.create_chain(&reorg_common_ancestor, 3, &mut rng).unwrap(); + assert_eq!(tf.best_block_id(), new_best_block); + + assert_eq!( + Some(order_data.clone()), + tf.chainstate.get_order_data(&order_id).unwrap() + ); + assert_eq!( + Some(order_data.ask().amount()), + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(order_data.give().amount()), + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + }); +} diff --git a/chainstate/tx-verifier/src/transaction_verifier/accounting_undo_cache/tests.rs b/chainstate/tx-verifier/src/transaction_verifier/accounting_undo_cache/tests.rs index 95b8340526..4e80612d76 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/accounting_undo_cache/tests.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/accounting_undo_cache/tests.rs @@ -24,6 +24,7 @@ use common::{ chain::{config::Builder as ConfigBuilder, Block}, primitives::H256, }; +use orders_accounting::OrdersAccountingDeltaUndoData; use pos_accounting::{DeltaMergeUndo, PoSAccountingUndo}; use test_utils::random::Seed; use tokens_accounting::TokensAccountingDeltaUndoData; @@ -89,6 +90,10 @@ fn connect_txs_in_hierarchy_default(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -192,6 +197,10 @@ fn connect_txs_in_hierarchy_disposable(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -273,6 +282,10 @@ fn connect_txs_in_hierarchy_twice(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -355,6 +368,10 @@ fn connect_reward_in_hierarchy_twice(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -465,6 +482,10 @@ fn disconnect_txs_in_hierarchy_default(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -566,6 +587,10 @@ fn disconnect_txs_in_hierarchy_disposable(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) diff --git a/chainstate/tx-verifier/src/transaction_verifier/flush.rs b/chainstate/tx-verifier/src/transaction_verifier/flush.rs index ad921f2746..2b77e1163a 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/flush.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/flush.rs @@ -18,6 +18,7 @@ use super::{ token_issuance_cache::{CachedAuxDataOp, CachedTokenIndexOp, ConsumedTokenIssuanceCache}, CachedOperation, TransactionVerifierDelta, }; +use orders_accounting::FlushableOrdersAccountingView; use tokens_accounting::FlushableTokensAccountingView; use utxo::FlushableUtxoView; @@ -66,6 +67,7 @@ pub fn flush_to_storage( where ::Error: From<::Error>, ::Error: From<::Error>, + ::Error: From<::Error>, ::Error: From, { flush_tokens(storage, &consumed.token_issuance_cache)?; @@ -122,5 +124,18 @@ where }; } + storage.batch_write_orders_data(consumed.orders_accounting_delta)?; + + // flush orders accounting block undo + for (tx_source, op) in consumed.orders_accounting_delta_undo { + match op { + CachedOperation::Write(undo) => { + storage.set_orders_accounting_undo_data(tx_source, &undo)? + } + CachedOperation::Read(_) => (), + CachedOperation::Erase => storage.del_orders_accounting_undo_data(tx_source)?, + } + } + Ok(()) } diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index c2ebd1cc87..1ebbefb612 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -867,16 +867,12 @@ where .map_err(|_| ConnectTransactionError::TxVerifierStorage) })?; - // FIXME: proper impl - let orders_store = orders_accounting::InMemoryOrdersAccounting::new(); - let orders_db = orders_accounting::OrdersAccountingDB::new(&orders_store); - // check for attempted money printing and invalid inputs/outputs combinations let fee = input_output_policy::check_tx_inputs_outputs_policy( tx.transaction(), self.chain_config.as_ref(), tx_source.expected_block_height(), - &orders_db, + &self.orders_accounting_cache, &self.pos_accounting_adapter.accounting_delta(), &self.utxo_cache, )?; diff --git a/chainstate/tx-verifier/src/transaction_verifier/storage.rs b/chainstate/tx-verifier/src/transaction_verifier/storage.rs index ec969a6b39..0e1b1d264d 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/storage.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/storage.rs @@ -62,6 +62,8 @@ pub enum TransactionVerifierStorageError { AccountingBlockUndoError(#[from] accounting::BlockUndoError), #[error("Tokens accounting error: {0}")] TokensAccountingError(#[from] tokens_accounting::Error), + #[error("Orders accounting error: {0}")] + OrdersAccountingError(#[from] orders_accounting::Error), } // TODO(Gosha): PoSAccountingView should be replaced with PoSAccountingStorageRead in which the diff --git a/chainstate/tx-verifier/src/transaction_verifier/tests/hierarchy_write.rs b/chainstate/tx-verifier/src/transaction_verifier/tests/hierarchy_write.rs index 7a50f83bd2..29fe1558d7 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/tests/hierarchy_write.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/tests/hierarchy_write.rs @@ -26,6 +26,7 @@ use common::chain::{ DelegationId, }; use mockall::predicate::eq; +use orders_accounting::OrdersAccountingDeltaUndoData; use pos_accounting::DeltaMergeUndo; use rstest::rstest; use test_utils::random::Seed; @@ -80,6 +81,10 @@ fn utxo_set_from_chain_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -159,6 +164,10 @@ fn tokens_set_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -261,6 +270,10 @@ fn utxo_del_from_chain_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -317,6 +330,10 @@ fn tokens_del_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -381,6 +398,10 @@ fn utxo_conflict_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -471,6 +492,10 @@ fn block_undo_from_chain_conflict_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -582,6 +607,10 @@ fn tokens_conflict_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -659,6 +688,10 @@ fn pos_accounting_stake_pool_set_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -730,6 +763,10 @@ fn pos_accounting_stake_pool_undo_set_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -837,6 +874,10 @@ fn pos_accounting_stake_pool_and_delegation_undo_set_hierarchy(#[case] seed: See .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -951,6 +992,10 @@ fn pos_accounting_stake_pool_undo_del_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -1027,6 +1072,10 @@ fn nonce_set_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -1083,6 +1132,10 @@ fn nonce_del_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -1164,6 +1217,10 @@ fn tokens_v1_set_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -1280,6 +1337,10 @@ fn tokens_v1_set_issue_and_lock_undo_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -1385,6 +1446,10 @@ fn tokens_v1_del_undo_hierarchy(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -1465,6 +1530,10 @@ fn utxo_set_from_chain_hierarchy_with_derived(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -1536,6 +1605,10 @@ fn utxo_del_from_chain_hierarchy_with_derived(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) diff --git a/chainstate/tx-verifier/src/transaction_verifier/utxos_undo_cache/tests.rs b/chainstate/tx-verifier/src/transaction_verifier/utxos_undo_cache/tests.rs index f0ff6e9dfb..6473fefa66 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/utxos_undo_cache/tests.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/utxos_undo_cache/tests.rs @@ -28,6 +28,7 @@ use common::{ chain::{config::Builder as ConfigBuilder, Block}, primitives::H256, }; +use orders_accounting::OrdersAccountingDeltaUndoData; use pos_accounting::DeltaMergeUndo; use test_utils::random::Seed; use tokens_accounting::TokensAccountingDeltaUndoData; @@ -103,6 +104,10 @@ fn connect_txs_in_hierarchy_default(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -215,6 +220,10 @@ fn connect_txs_in_hierarchy_disposable(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -299,6 +308,10 @@ fn connect_txs_in_hierarchy_twice(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -381,6 +394,10 @@ fn connect_reward_in_hierarchy_twice(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -506,6 +523,10 @@ fn disconnect_txs_in_hierarchy_default(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) @@ -664,6 +685,10 @@ fn disconnect_txs_in_hierarchy_disposable(#[case] seed: Seed) { .expect_batch_write_tokens_data() .times(1) .return_const(Ok(TokensAccountingDeltaUndoData::new())); + store + .expect_batch_write_orders_data() + .times(1) + .return_const(Ok(OrdersAccountingDeltaUndoData::new())); store .expect_batch_write_delta() .times(1) diff --git a/common/src/chain/upgrades/chainstate_upgrade.rs b/common/src/chain/upgrades/chainstate_upgrade.rs index fa44312988..f0773ede2f 100644 --- a/common/src/chain/upgrades/chainstate_upgrade.rs +++ b/common/src/chain/upgrades/chainstate_upgrade.rs @@ -86,3 +86,5 @@ impl ChainstateUpgrade { } impl Activate for ChainstateUpgrade {} + +// FIXME: fork for orders diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index f76aa3814c..e1e6fa1bd4 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -265,6 +265,7 @@ impl MempoolBanScore for TransactionVerifierStorageError { TransactionVerifierStorageError::UtxoError(err) => err.mempool_ban_score(), TransactionVerifierStorageError::PoSAccountingError(err) => err.mempool_ban_score(), TransactionVerifierStorageError::TokensAccountingError(err) => err.mempool_ban_score(), + TransactionVerifierStorageError::OrdersAccountingError(err) => err.mempool_ban_score(), // Should not happen in mempool (no undos, no block processing, internal errors) TransactionVerifierStorageError::GetAncestorError(_) => 0, diff --git a/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs b/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs index 729801cb90..76602b4c1c 100644 --- a/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs +++ b/mempool/src/pool/tx_pool/tx_verifier/chainstate_handle.rs @@ -61,6 +61,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: orders_accounting::Error) -> Self { + Error::from(ChainstateError::from(chainstate::BlockError::from(e))) + } +} + impl From for Error { fn from(e: utxo::Error) -> Self { chainstate::tx_verifier::TransactionVerifierStorageError::from(e).into() diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 62159f8687..5b349111ac 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -193,8 +193,8 @@ impl OrdersAccountingOperations for OrdersAccountingCac .order_data .merge_delta_data_element(id, accounting::DataDelta::new(Some(order_data), None))?; - self.data.ask_balances.add_unsigned(id, ask_balance)?; - self.data.give_balances.add_unsigned(id, give_balance)?; + self.data.ask_balances.sub_unsigned(id, ask_balance)?; + self.data.give_balances.sub_unsigned(id, give_balance)?; Ok(OrdersAccountingUndo::WithdrawOrder(WithdrawOrderUndo { id, diff --git a/orders-accounting/src/data.rs b/orders-accounting/src/data.rs index 3b6e781525..2e0ba0c3d3 100644 --- a/orders-accounting/src/data.rs +++ b/orders-accounting/src/data.rs @@ -85,3 +85,13 @@ pub struct OrdersAccountingDeltaUndoData { pub(crate) ask_balances: DeltaAmountCollection, pub(crate) give_balances: DeltaAmountCollection, } + +impl OrdersAccountingDeltaUndoData { + pub fn new() -> Self { + Self { + order_data: DeltaDataUndoCollection::new(), + ask_balances: DeltaAmountCollection::new(), + give_balances: DeltaAmountCollection::new(), + } + } +} diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index 96f66e705d..7f0849e952 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -23,7 +23,7 @@ mod view; pub use { cache::OrdersAccountingCache, - data::{OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}, + data::{OrdersAccountingData, OrdersAccountingDeltaData, OrdersAccountingDeltaUndoData}, error::Error, operations::{OrdersAccountingOperations, OrdersAccountingUndo}, price_calculation::calculate_fill_order, diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index f9499185ba..c35c056bd1 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -25,7 +25,7 @@ use test_utils::random::{make_seedable_rng, Seed}; use crate::{ cache::OrdersAccountingCache, operations::OrdersAccountingOperations, - view::FlushableOrdersAccountingView, InMemoryOrdersAccounting, OrdersAccountingDB, + view::FlushableOrdersAccountingView, Error, InMemoryOrdersAccounting, OrdersAccountingDB, OrdersAccountingView, }; @@ -64,6 +64,44 @@ fn create_order_and_flush(#[case] seed: Seed) { assert_eq!(expected_storage, storage); } +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_order_twice(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let order_data = make_order_data(&mut rng); + + { + let storage = InMemoryOrdersAccounting::new(); + let db = OrdersAccountingDB::new(&storage); + let mut cache = OrdersAccountingCache::new(&db); + + let _ = cache.create_order(order_id, order_data.clone()).unwrap(); + + assert_eq!( + cache.create_order(order_id, order_data.clone()), + Err(Error::OrderAlreadyExists(order_id)) + ); + } + + { + let storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + let db = OrdersAccountingDB::new(&storage); + let mut cache = OrdersAccountingCache::new(&db); + + assert_eq!( + cache.create_order(order_id, order_data.clone()), + Err(Error::OrderAlreadyExists(order_id)) + ); + } +} + #[rstest] #[trace] #[case(Seed::from_entropy())] @@ -103,6 +141,382 @@ fn create_order_and_undo(#[case] seed: Seed) { assert_eq!(InMemoryOrdersAccounting::new(), storage); } +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn withdraw_order_and_flush(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let order_data = make_order_data(&mut rng); + + let mut storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + // try to withdraw non-existing order + { + let random_order = OrderId::random_using(&mut rng); + let result = cache.withdraw_order(random_order); + assert_eq!( + result.unwrap_err(), + Error::AttemptedWithdrawNonexistingOrderData(random_order) + ); + } + + let _ = cache.withdraw_order(order_id).unwrap(); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + assert_eq!(InMemoryOrdersAccounting::new(), storage); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn withdraw_order_twice(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let order_data = make_order_data(&mut rng); + + let storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + let db = OrdersAccountingDB::new(&storage); + let mut cache = OrdersAccountingCache::new(&db); + + let _ = cache.withdraw_order(order_id).unwrap(); + + assert_eq!( + cache.withdraw_order(order_id,), + Err(Error::AttemptedWithdrawNonexistingOrderData(order_id)) + ); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn withdraw_order_and_undo(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let order_data = make_order_data(&mut rng); + + let mut storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + let original_storage = storage.clone(); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + let undo = cache.withdraw_order(order_id).unwrap(); + + assert_eq!(None, cache.get_order_data(&order_id).unwrap().as_ref()); + assert_eq!( + Some(Amount::ZERO), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::ZERO), + cache.get_give_balance(&order_id).unwrap() + ); + + cache.undo(undo).unwrap(); + + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(order_data.ask().amount()), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(order_data.give().amount()), + cache.get_give_balance(&order_id).unwrap() + ); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + assert_eq!(original_storage, storage); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_entire_order_and_flush(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let order_data = make_order_data(&mut rng); + + let mut storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + // try to fill non-existing order + { + let random_order = OrderId::random_using(&mut rng); + let result = cache.fill_order(random_order, order_data.ask().clone()); + assert_eq!(result.unwrap_err(), Error::OrderDataNotFound(random_order)); + } + + // try to overfill + { + let fill = OutputValue::Coin((order_data.ask().amount() + Amount::from_atoms(1)).unwrap()); + let result = cache.fill_order(order_id, fill); + assert_eq!(result.unwrap_err(), Error::OrderOverflow(order_id)); + } + + let _ = cache.fill_order(order_id, order_data.ask().clone()).unwrap(); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + assert_eq!(InMemoryOrdersAccounting::new(), storage); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_order_partially_and_flush(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let token_id = TokenId::random_using(&mut rng); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, Amount::from_atoms(3)), + OutputValue::Coin(Amount::from_atoms(10)), + ); + + let mut storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + let _ = cache + .fill_order( + order_id, + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + ) + .unwrap(); + + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(Amount::from_atoms(2)), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::from_atoms(7)), + cache.get_give_balance(&order_id).unwrap() + ); + + let _ = cache + .fill_order( + order_id, + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + ) + .unwrap(); + + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(Amount::from_atoms(1)), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::from_atoms(4)), + cache.get_give_balance(&order_id).unwrap() + ); + + let _ = cache + .fill_order( + order_id, + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + ) + .unwrap(); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + assert_eq!(InMemoryOrdersAccounting::new(), storage); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_order_partially_and_undo(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let token_id = TokenId::random_using(&mut rng); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, Amount::from_atoms(3)), + OutputValue::Coin(Amount::from_atoms(10)), + ); + + let mut storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + let original_storage = storage.clone(); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + let undo1 = cache + .fill_order( + order_id, + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + ) + .unwrap(); + + let undo2 = cache + .fill_order( + order_id, + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + ) + .unwrap(); + + let undo3 = cache + .fill_order( + order_id, + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + ) + .unwrap(); + + assert_eq!(None, cache.get_order_data(&order_id).unwrap().as_ref()); + assert_eq!( + Some(Amount::ZERO), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::ZERO), + cache.get_give_balance(&order_id).unwrap() + ); + + cache.undo(undo3).unwrap(); + + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(Amount::from_atoms(1)), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::from_atoms(4)), + cache.get_give_balance(&order_id).unwrap() + ); + + cache.undo(undo2).unwrap(); + + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(Amount::from_atoms(2)), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::from_atoms(7)), + cache.get_give_balance(&order_id).unwrap() + ); + + cache.undo(undo1).unwrap(); + + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(Amount::from_atoms(3)), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::from_atoms(10)), + cache.get_give_balance(&order_id).unwrap() + ); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + assert_eq!(original_storage, storage); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_order_partially_and_withdraw(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let token_id = TokenId::random_using(&mut rng); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, Amount::from_atoms(3)), + OutputValue::Coin(Amount::from_atoms(10)), + ); + + let mut storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, order_data.ask().amount())]), + BTreeMap::from_iter([(order_id, order_data.give().amount())]), + ); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + let _ = cache + .fill_order( + order_id, + OutputValue::TokenV1(token_id, Amount::from_atoms(1)), + ) + .unwrap(); + + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(Amount::from_atoms(2)), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::from_atoms(7)), + cache.get_give_balance(&order_id).unwrap() + ); + + let _ = cache.withdraw_order(order_id).unwrap(); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + assert_eq!(InMemoryOrdersAccounting::new(), storage); +} + +// If total give balance of an order is split into a random number of fill operations +// they must exhaust the order entirely without any change left. #[rstest] #[trace] #[case(Seed::from_entropy())] @@ -134,5 +548,3 @@ fn fill_order_must_converge(#[case] seed: Seed) { assert_eq!(InMemoryOrdersAccounting::new(), storage); } - -// FIXME: more tests diff --git a/pos-accounting/src/lib.rs b/pos-accounting/src/lib.rs index d4a3278e2b..3c191298c2 100644 --- a/pos-accounting/src/lib.rs +++ b/pos-accounting/src/lib.rs @@ -20,7 +20,7 @@ mod storage; pub use crate::{ data::PoSAccountingData, - error::Error, + error::{Error, Result}, pool::{ delegation::DelegationData, delta::{data::PoSAccountingDeltaData, DeltaMergeUndo, PoSAccountingDelta}, From 9dea16e95dd2853e5661bfa34e4fd74ac564e12a Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 5 May 2024 14:32:57 +0300 Subject: [PATCH 15/37] Fix purposes tests --- .../src/tests/constraints_tests.rs | 6 --- .../src/tests/homomorphism.rs | 1 - .../input_output_policy/purposes_check.rs | 1 - .../tests/constraints_tests.rs | 2 - .../tests/outputs_utils.rs | 46 ++++++++++++++--- .../tests/purpose_tests.rs | 51 +++++++++++++++++++ .../src/transaction_verifier/mod.rs | 3 +- 7 files changed, 91 insertions(+), 19 deletions(-) diff --git a/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs b/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs index 9cd6a55159..bbfc176802 100644 --- a/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs +++ b/chainstate/constraints-value-accumulator/src/tests/constraints_tests.rs @@ -76,7 +76,6 @@ fn allow_fees_from_decommission(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); @@ -145,7 +144,6 @@ fn allow_fees_from_spend_share(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); @@ -213,7 +211,6 @@ fn no_timelock_outputs_on_decommission(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); @@ -322,7 +319,6 @@ fn try_to_unlock_coins_with_smaller_timelock(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); @@ -466,7 +462,6 @@ fn check_timelock_saturation(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); @@ -586,7 +581,6 @@ fn try_to_overspend_on_spending_delegation(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); diff --git a/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs b/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs index c14b5bc50f..c7cab3ef28 100644 --- a/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs +++ b/chainstate/constraints-value-accumulator/src/tests/homomorphism.rs @@ -102,7 +102,6 @@ fn accumulators_homomorphism(#[case] seed: Seed) { ); let pos_db = PoSAccountingDB::new(&pos_store); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs index 09bb235b70..2040d04aa5 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs @@ -188,7 +188,6 @@ pub fn check_tx_inputs_outputs_purposes( }) .count(); - // FIXME: allow tokens and order commands? ensure!( account_commands_count <= 1, IOPolicyError::MultipleAccountCommands diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/constraints_tests.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/constraints_tests.rs index b698f35411..6b7a003fc8 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/constraints_tests.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/constraints_tests.rs @@ -102,7 +102,6 @@ fn timelock_constraints_on_decommission_in_tx(#[case] seed: Seed) { ); let pos_db = pos_accounting::PoSAccountingDB::new(&pos_store); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); @@ -253,7 +252,6 @@ fn timelock_constraints_on_spend_share_in_tx(#[case] seed: Seed) { let pos_db = pos_accounting::PoSAccountingDB::new(&pos_store); let utxo_db = UtxosDBInMemoryImpl::new(Id::::new(H256::zero()), BTreeMap::new()); - // FIXME: proper impl let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs index 9e4c93d8a5..b65085066b 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs @@ -23,8 +23,8 @@ use common::{ IsTokenFreezable, IsTokenUnfreezable, Metadata, NftIssuance, NftIssuanceV0, TokenId, TokenIssuance, TokenIssuanceV1, TokenTotalSupply, }, - AccountCommand, AccountNonce, AccountSpending, DelegationId, Destination, PoolId, TxInput, - TxOutput, + AccountCommand, AccountNonce, AccountSpending, DelegationId, Destination, OrderData, + OrderId, PoolId, TxInput, TxOutput, }, primitives::{per_thousand::PerThousand, Amount, H256}, }; @@ -51,7 +51,7 @@ fn update_functions_below_if_new_outputs_were_added(output: TxOutput) { } } -pub fn all_outputs() -> [TxOutput; 11] { +pub fn all_outputs() -> [TxOutput; 12] { [ transfer(), htlc(), @@ -64,10 +64,11 @@ pub fn all_outputs() -> [TxOutput; 11] { issue_tokens(), issue_nft(), data_deposit(), + create_order(), ] } -pub fn valid_tx_outputs() -> [TxOutput; 10] { +pub fn valid_tx_outputs() -> [TxOutput; 11] { [ transfer(), htlc(), @@ -79,6 +80,7 @@ pub fn valid_tx_outputs() -> [TxOutput; 10] { issue_tokens(), issue_nft(), data_deposit(), + create_order(), ] } @@ -93,11 +95,18 @@ pub fn valid_tx_inputs_utxos() -> [TxOutput; 6] { ] } -pub fn invalid_tx_inputs_utxos() -> [TxOutput; 5] { - [burn(), delegate_staking(), create_delegation(), issue_tokens(), data_deposit()] +pub fn invalid_tx_inputs_utxos() -> [TxOutput; 6] { + [ + burn(), + delegate_staking(), + create_delegation(), + issue_tokens(), + data_deposit(), + create_order(), + ] } -pub fn invalid_block_reward_for_pow() -> [TxOutput; 10] { +pub fn invalid_block_reward_for_pow() -> [TxOutput; 11] { [ transfer(), htlc(), @@ -109,10 +118,11 @@ pub fn invalid_block_reward_for_pow() -> [TxOutput; 10] { issue_nft(), issue_tokens(), data_deposit(), + create_order(), ] } -pub fn all_account_inputs() -> [TxInput; 7] { +pub fn all_account_inputs() -> [TxInput; 9] { [ TxInput::from_account( AccountNonce::new(0), @@ -142,6 +152,18 @@ pub fn all_account_inputs() -> [TxInput; 7] { AccountNonce::new(0), AccountCommand::ChangeTokenAuthority(TokenId::zero(), Destination::AnyoneCanSpend), ), + TxInput::from_command( + AccountNonce::new(0), + AccountCommand::WithdrawOrder(OrderId::zero()), + ), + TxInput::from_command( + AccountNonce::new(0), + AccountCommand::FillOrder( + OrderId::zero(), + OutputValue::Coin(Amount::ZERO), + Destination::AnyoneCanSpend, + ), + ), ] } @@ -215,6 +237,14 @@ pub fn data_deposit() -> TxOutput { TxOutput::DataDeposit(vec![]) } +pub fn create_order() -> TxOutput { + TxOutput::CreateOrder(OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(Amount::ZERO), + OutputValue::Coin(Amount::ZERO), + )) +} + pub fn issue_nft() -> TxOutput { TxOutput::IssueNft( TokenId::new(H256::zero()), diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/purpose_tests.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/purpose_tests.rs index dbd56ade49..26e54c31e5 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/purpose_tests.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/purpose_tests.rs @@ -119,6 +119,7 @@ fn tx_many_to_many_valid(#[case] seed: Seed) { issue_tokens(), issue_nft(), data_deposit(), + create_order(), ]; let (utxo_db, tx) = prepare_utxos_and_tx_with_random_combinations( @@ -133,6 +134,56 @@ fn tx_many_to_many_valid(#[case] seed: Seed) { check_tx_inputs_outputs_purposes(&tx, &inputs_utxos).unwrap(); } +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn tx_many_to_many_valid_with_account_input(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + let number_of_inputs = rng.gen_range(1..10); + let number_of_outputs = rng.gen_range(1..10); + + let account_inputs = all_account_inputs(); + // stake pool and create delegation are skipped to avoid dealing with uniqueness + let valid_outputs = [ + lock_then_transfer(), + transfer(), + burn(), + delegate_staking(), + issue_tokens(), + issue_nft(), + data_deposit(), + create_order(), + ]; + + let inputs_utxos = get_random_outputs_combination( + &mut rng, + &super::outputs_utils::valid_tx_inputs_utxos(), + number_of_inputs, + ); + + let inputs = { + let mut inputs = inputs_utxos + .iter() + .enumerate() + .map(|(i, _)| { + TxInput::from_utxo( + OutPointSourceId::Transaction(Id::new(H256::zero())), + i as u32, + ) + }) + .chain(get_random_inputs_combination(&mut rng, &account_inputs, 1)) + .collect::>(); + inputs.shuffle(&mut rng); + inputs + }; + + let outputs = get_random_outputs_combination(&mut rng, &valid_outputs, number_of_outputs); + + let tx = Transaction::new(0, inputs, outputs).unwrap(); + + check_tx_inputs_outputs_purposes(&tx, &inputs_utxos).unwrap(); +} + #[rstest] #[trace] #[case(Seed::from_entropy())] diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 1ebbefb612..1133102d11 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -789,7 +789,8 @@ where | TxOutput::LockThenTransfer(..) | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) - | TxOutput::IssueFungibleToken(..) => None, + | TxOutput::IssueFungibleToken(..) + | TxOutput::Htlc(_, _) => None, | TxOutput::CreateOrder(order_data) => match input_utxo_outpoint { Some(input_utxo_outpoint) => { let order_id = make_order_id(&input_utxo_outpoint); From 7fdf792ac02fe93059951cbb0df657a8037c49fb Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 5 May 2024 14:54:13 +0300 Subject: [PATCH 16/37] Fix bunch of todos --- chainstate/src/detail/ban_score.rs | 46 +++++++++---------- .../interface/chainstate_interface_impl.rs | 4 +- chainstate/src/rpc/types/account.rs | 22 +++++++-- chainstate/src/rpc/types/output.rs | 13 +++++- .../test-framework/src/random_tx_maker.rs | 4 +- common/src/chain/config/mod.rs | 10 ++++ common/src/chain/order.rs | 3 +- common/src/chain/transaction/output/mod.rs | 7 ++- .../src/chain/upgrades/chainstate_upgrade.rs | 2 - mempool/src/error/ban_score.rs | 44 +++++++++--------- mempool/src/pool/entry.rs | 6 ++- mempool/src/pool/orphans/mod.rs | 4 +- orders-accounting/src/cache.rs | 2 - 13 files changed, 103 insertions(+), 64 deletions(-) diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index fc612a8973..cee0ed2089 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -589,7 +589,7 @@ impl BanScore for constraints_value_accumulator::Error { constraints_value_accumulator::Error::DelegationBalanceNotFound(_) => 0, constraints_value_accumulator::Error::AccountBalanceNotFound(_) => 0, constraints_value_accumulator::Error::NegativeAccountBalance(_) => 100, - constraints_value_accumulator::Error::OrdersAccountingError(_) => todo!(), + constraints_value_accumulator::Error::OrdersAccountingError(err) => err.ban_score(), } } } @@ -655,28 +655,28 @@ impl BanScore for orders_accounting::Error { fn ban_score(&self) -> u32 { use orders_accounting::Error; match self { - Error::StorageError(_) => todo!(), - Error::AccountingError(_) => todo!(), - Error::OrderAlreadyExists(_) => todo!(), - Error::OrderDataNotFound(_) => todo!(), - Error::OrderAskBalanceNotFound(_) => todo!(), - Error::OrderGiveBalanceNotFound(_) => todo!(), - Error::InvariantOrderDataNotFoundForUndo(_) => todo!(), - Error::InvariantOrderAskBalanceNotFoundForUndo(_) => todo!(), - Error::InvariantOrderAskBalanceChangedForUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceChangedForUndo(_) => todo!(), - Error::InvariantOrderDataExistForWithdrawUndo(_) => todo!(), - Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => todo!(), - Error::FillOrderChangeLeft(_) => todo!(), - Error::CurrencyMismatch => todo!(), - Error::OrderOverflow(_) => todo!(), - Error::AttemptedWithdrawNonexistingOrderData(_) => todo!(), - Error::AttemptedWithdrawNonexistingAskBalance(_) => todo!(), - Error::AttemptedWithdrawNonexistingGiveBalance(_) => todo!(), - Error::ViewFail => todo!(), - Error::StorageWrite => todo!(), + Error::StorageError(_) => 0, + Error::AccountingError(_) => 100, + Error::OrderAlreadyExists(_) => 100, + Error::OrderDataNotFound(_) => 100, + Error::OrderAskBalanceNotFound(_) => 100, + Error::OrderGiveBalanceNotFound(_) => 100, + Error::InvariantOrderDataNotFoundForUndo(_) => 100, + Error::InvariantOrderAskBalanceNotFoundForUndo(_) => 100, + Error::InvariantOrderAskBalanceChangedForUndo(_) => 100, + Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => 100, + Error::InvariantOrderGiveBalanceChangedForUndo(_) => 100, + Error::InvariantOrderDataExistForWithdrawUndo(_) => 100, + Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => 100, + Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => 100, + Error::FillOrderChangeLeft(_) => 100, + Error::CurrencyMismatch => 100, + Error::OrderOverflow(_) => 100, + Error::AttemptedWithdrawNonexistingOrderData(_) => 100, + Error::AttemptedWithdrawNonexistingAskBalance(_) => 100, + Error::AttemptedWithdrawNonexistingGiveBalance(_) => 100, + Error::ViewFail => 0, + Error::StorageWrite => 0, } } } diff --git a/chainstate/src/interface/chainstate_interface_impl.rs b/chainstate/src/interface/chainstate_interface_impl.rs index 7bbdd987a2..50f290a854 100644 --- a/chainstate/src/interface/chainstate_interface_impl.rs +++ b/chainstate/src/interface/chainstate_interface_impl.rs @@ -831,8 +831,8 @@ fn get_output_coin_amount( TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) - | TxOutput::DataDeposit(_) => None, - TxOutput::CreateOrder(_) => todo!(), + | TxOutput::DataDeposit(_) + | TxOutput::CreateOrder(_) => None, }; Ok(amount) diff --git a/chainstate/src/rpc/types/account.rs b/chainstate/src/rpc/types/account.rs index db0bfcea43..82731fd19d 100644 --- a/chainstate/src/rpc/types/account.rs +++ b/chainstate/src/rpc/types/account.rs @@ -17,11 +17,13 @@ use common::{ address::{AddressError, RpcAddress}, chain::{ tokens::{IsTokenUnfreezable, TokenId}, - AccountCommand, AccountSpending, ChainConfig, DelegationId, Destination, + AccountCommand, AccountSpending, ChainConfig, DelegationId, Destination, OrderId, }, primitives::amount::RpcAmountOut, }; +use super::output::RpcOutputValue; + #[derive(Debug, Clone, serde::Serialize, rpc_description::HasValueHint)] #[serde(tag = "type", content = "content")] pub enum RpcAccountSpending { @@ -72,6 +74,14 @@ pub enum RpcAccountCommand { token_id: RpcAddress, new_authority: RpcAddress, }, + WithdrawOrder { + order_id: RpcAddress, + }, + FillOrder { + order_id: RpcAddress, + fill_value: RpcOutputValue, + destination: RpcAddress, + }, } impl RpcAccountCommand { @@ -103,8 +113,14 @@ impl RpcAccountCommand { new_authority: RpcAddress::new(chain_config, destination.clone())?, } } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + AccountCommand::WithdrawOrder(id) => RpcAccountCommand::WithdrawOrder { + order_id: RpcAddress::new(chain_config, *id)?, + }, + AccountCommand::FillOrder(id, fill, dest) => RpcAccountCommand::FillOrder { + order_id: RpcAddress::new(chain_config, *id)?, + fill_value: RpcOutputValue::new(chain_config, fill.clone())?, + destination: RpcAddress::new(chain_config, dest.clone())?, + }, }; Ok(result) } diff --git a/chainstate/src/rpc/types/output.rs b/chainstate/src/rpc/types/output.rs index 31cd91ac5b..1b1dd15423 100644 --- a/chainstate/src/rpc/types/output.rs +++ b/chainstate/src/rpc/types/output.rs @@ -40,7 +40,7 @@ pub enum RpcOutputValue { } impl RpcOutputValue { - fn new(chain_config: &ChainConfig, value: OutputValue) -> Result { + pub fn new(chain_config: &ChainConfig, value: OutputValue) -> Result { let result = match value { OutputValue::Coin(amount) => RpcOutputValue::Coin { amount: RpcAmountOut::from_amount(amount, chain_config.coin_decimals()), @@ -151,6 +151,11 @@ pub enum RpcTxOutput { value: RpcOutputValue, htlc: RpcHashedTimelockContract, }, + CreateOrder { + authority: RpcAddress, + ask_value: RpcOutputValue, + give_value: RpcOutputValue, + }, } impl RpcTxOutput { @@ -203,7 +208,11 @@ impl RpcTxOutput { TxOutput::DataDeposit(data) => RpcTxOutput::DataDeposit { data: RpcHexString::from_bytes(data), }, - TxOutput::CreateOrder(_) => todo!(), + TxOutput::CreateOrder(data) => RpcTxOutput::CreateOrder { + authority: RpcAddress::new(chain_config, data.authority().clone())?, + ask_value: RpcOutputValue::new(chain_config, data.ask().clone())?, + give_value: RpcOutputValue::new(chain_config, data.give().clone())?, + }, }; Ok(result) } diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index 057e72b999..e80fecc966 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -410,7 +410,7 @@ impl<'a> RandomTxMaker<'a> { result_inputs.extend(inputs); result_outputs.extend(outputs); } - AccountType::Order(_) => todo!(), + AccountType::Order(_) => unimplemented!(), } } @@ -1050,7 +1050,7 @@ impl<'a> RandomTxMaker<'a> { *dummy_token_id = make_token_id(inputs).unwrap(); Some(output) } - TxOutput::CreateOrder(_) => todo!(), + TxOutput::CreateOrder(_) => unimplemented!(), }) .collect(); diff --git a/common/src/chain/config/mod.rs b/common/src/chain/config/mod.rs index e6bf7ff89e..6aaedd80b2 100644 --- a/common/src/chain/config/mod.rs +++ b/common/src/chain/config/mod.rs @@ -329,6 +329,16 @@ impl ChainConfig { } } + #[must_use] + pub fn order_id_address_prefix(&self) -> &'static str { + match self.chain_type { + ChainType::Mainnet => "mordr", + ChainType::Testnet => "tordr", + ChainType::Regtest => "rordr", + ChainType::Signet => "sordr", + } + } + #[must_use] pub fn vrf_public_key_address_prefix(&self) -> &'static str { match self.chain_type { diff --git a/common/src/chain/order.rs b/common/src/chain/order.rs index 9e2132a9df..5fa50b8ec9 100644 --- a/common/src/chain/order.rs +++ b/common/src/chain/order.rs @@ -42,8 +42,7 @@ impl Addressable for OrderId { type Error = AddressError; fn address_prefix(&self, chain_config: &ChainConfig) -> &str { - //chain_config.order_id_address_prefix() - todo!() + chain_config.order_id_address_prefix() } fn encode_to_bytes_for_address(&self) -> Vec { diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs index 708a4d1401..21c00f1520 100644 --- a/common/src/chain/transaction/output/mod.rs +++ b/common/src/chain/transaction/output/mod.rs @@ -330,7 +330,12 @@ impl TextSummary for TxOutput { fmt_dest(&htlc.refund_key) ) } - TxOutput::CreateOrder(_) => todo!(), + TxOutput::CreateOrder(order) => format!( + "CreateOrder(Authority({}), AskValue({}), GiveValue({}))", + fmt_dest(order.authority()), + fmt_val(order.ask()), + fmt_val(order.give()), + ), } } } diff --git a/common/src/chain/upgrades/chainstate_upgrade.rs b/common/src/chain/upgrades/chainstate_upgrade.rs index f0773ede2f..fa44312988 100644 --- a/common/src/chain/upgrades/chainstate_upgrade.rs +++ b/common/src/chain/upgrades/chainstate_upgrade.rs @@ -86,5 +86,3 @@ impl ChainstateUpgrade { } impl Activate for ChainstateUpgrade {} - -// FIXME: fork for orders diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index e1e6fa1bd4..c69128c911 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -494,28 +494,28 @@ impl MempoolBanScore for orders_accounting::Error { use orders_accounting::Error; match self { - Error::StorageError(_) => todo!(), - Error::AccountingError(_) => todo!(), - Error::OrderAlreadyExists(_) => todo!(), - Error::OrderDataNotFound(_) => todo!(), - Error::OrderAskBalanceNotFound(_) => todo!(), - Error::OrderGiveBalanceNotFound(_) => todo!(), - Error::InvariantOrderDataNotFoundForUndo(_) => todo!(), - Error::InvariantOrderAskBalanceNotFoundForUndo(_) => todo!(), - Error::InvariantOrderAskBalanceChangedForUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceChangedForUndo(_) => todo!(), - Error::InvariantOrderDataExistForWithdrawUndo(_) => todo!(), - Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => todo!(), - Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => todo!(), - Error::FillOrderChangeLeft(_) => todo!(), - Error::CurrencyMismatch => todo!(), - Error::OrderOverflow(_) => todo!(), - Error::AttemptedWithdrawNonexistingOrderData(_) => todo!(), - Error::AttemptedWithdrawNonexistingAskBalance(_) => todo!(), - Error::AttemptedWithdrawNonexistingGiveBalance(_) => todo!(), - Error::ViewFail => todo!(), - Error::StorageWrite => todo!(), + Error::StorageError(_) => 0, + Error::AccountingError(_) => 100, + Error::OrderAlreadyExists(_) => 100, + Error::OrderDataNotFound(_) => 0, + Error::OrderAskBalanceNotFound(_) => 0, + Error::OrderGiveBalanceNotFound(_) => 0, + Error::InvariantOrderDataNotFoundForUndo(_) => 100, + Error::InvariantOrderAskBalanceNotFoundForUndo(_) => 100, + Error::InvariantOrderAskBalanceChangedForUndo(_) => 100, + Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => 100, + Error::InvariantOrderGiveBalanceChangedForUndo(_) => 100, + Error::InvariantOrderDataExistForWithdrawUndo(_) => 100, + Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => 100, + Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => 100, + Error::FillOrderChangeLeft(_) => 100, + Error::CurrencyMismatch => 100, + Error::OrderOverflow(_) => 100, + Error::AttemptedWithdrawNonexistingOrderData(_) => 0, + Error::AttemptedWithdrawNonexistingAskBalance(_) => 0, + Error::AttemptedWithdrawNonexistingGiveBalance(_) => 0, + Error::ViewFail => 0, + Error::StorageWrite => 0, } } } diff --git a/mempool/src/pool/entry.rs b/mempool/src/pool/entry.rs index e909f5e8ee..932a74801a 100644 --- a/mempool/src/pool/entry.rs +++ b/mempool/src/pool/entry.rs @@ -44,6 +44,7 @@ impl TxAccountDependency { pub enum TxDependency { DelegationAccount(TxAccountDependency), TokenSupplyAccount(TxAccountDependency), + OrderAccount(TxAccountDependency), TxOutput(Id, u32), // TODO: Block reward? } @@ -74,8 +75,9 @@ impl TxDependency { | AccountCommand::ChangeTokenAuthority(_, _) => { Self::TokenSupplyAccount(TxAccountDependency::new(acct.clone().into(), nonce)) } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => { + Self::OrderAccount(TxAccountDependency::new(acct.clone().into(), nonce)) + } } } diff --git a/mempool/src/pool/orphans/mod.rs b/mempool/src/pool/orphans/mod.rs index 1931b95fb8..b6eebbf3e3 100644 --- a/mempool/src/pool/orphans/mod.rs +++ b/mempool/src/pool/orphans/mod.rs @@ -304,7 +304,9 @@ impl<'p> PoolEntry<'p> { let entry = self.get(); !entry.requires().any(|dep| match dep { // Always consider account deps. TODO: can be optimized in the future - TxDependency::DelegationAccount(_) | TxDependency::TokenSupplyAccount(_) => false, + TxDependency::DelegationAccount(_) + | TxDependency::TokenSupplyAccount(_) + | TxDependency::OrderAccount(_) => false, TxDependency::TxOutput(tx_id, _) => self.pool.maps.by_tx_id.contains_key(&tx_id), }) } diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 5b349111ac..079e7868b3 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -157,8 +157,6 @@ impl OrdersAccountingOperations for OrdersAccountingCac return Err(Error::OrderAlreadyExists(id)); } - // FIXME: ask type != give ? - // FIXME: forbid 0 value? let ask_value = data.ask().clone(); let give_value = data.give().clone(); let undo_data = self From e61f50fab7dada6f44803423b4e59706a5ce005e Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 5 May 2024 15:05:06 +0300 Subject: [PATCH 17/37] Rename to TxOutput::AnyoneCanTake --- .../scanner-lib/src/blockchain_state/mod.rs | 16 ++++++++-------- .../scanner-lib/src/sync/tests/simulation.rs | 8 ++++---- api-server/web-server/src/api/json_helpers.rs | 2 +- .../src/constraints_accumulator.rs | 4 ++-- .../src/tests/orders_constraints.rs | 10 +++++----- .../src/detail/chainstateref/epoch_seal.rs | 2 +- chainstate/src/detail/chainstateref/mod.rs | 4 ++-- chainstate/src/detail/mod.rs | 2 +- chainstate/src/detail/query.rs | 2 +- .../src/interface/chainstate_interface_impl.rs | 2 +- chainstate/src/rpc/types/output.rs | 6 +++--- chainstate/test-framework/src/key_manager.rs | 2 +- chainstate/test-framework/src/random_tx_maker.rs | 6 +++--- .../src/signature_destination_getter.rs | 4 ++-- chainstate/test-framework/src/utils.rs | 6 +++--- chainstate/test-suite/src/tests/orders_tests.rs | 16 ++++++++-------- .../transaction_verifier/check_transaction.rs | 8 ++++---- .../input_output_policy/mod.rs | 4 ++-- .../input_output_policy/purposes_check.rs | 10 +++++----- .../input_output_policy/tests/outputs_utils.rs | 8 ++++---- .../tx-verifier/src/transaction_verifier/mod.rs | 12 ++++++------ .../transaction_verifier/token_issuance_cache.rs | 2 +- common/src/chain/order.rs | 10 +++++----- common/src/chain/tokens/tokens_utils.rs | 4 ++-- common/src/chain/transaction/output/mod.rs | 10 +++++----- .../src/chain/transaction/signature/tests/mod.rs | 2 +- .../signature/tests/sign_and_mutate.rs | 2 +- common/src/size_estimation/mod.rs | 2 +- consensus/src/pos/block_sig.rs | 2 +- consensus/src/pos/mod.rs | 2 +- mempool/src/pool/tx_pool/store/mem_usage.rs | 2 +- mintscript/src/translate.rs | 6 +++--- utxo/src/cache.rs | 2 +- wallet/src/account/currency_grouper/mod.rs | 6 +++--- wallet/src/account/mod.rs | 8 ++++---- wallet/src/account/output_cache/mod.rs | 12 ++++++------ wallet/src/account/transaction_list/mod.rs | 2 +- wallet/src/account/utxo_selector/output_group.rs | 2 +- wallet/src/send_request/mod.rs | 2 +- wallet/types/src/utxo_types.rs | 2 +- wallet/wallet-controller/src/lib.rs | 2 +- 41 files changed, 108 insertions(+), 108 deletions(-) diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 70cc489b54..8d2a79c1d7 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -397,7 +397,7 @@ async fn update_tables_from_block_reward( | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => {} + | TxOutput::AnyoneCanTake(_) => {} TxOutput::ProduceBlockFromStake(_, _) => { set_utxo( outpoint, @@ -573,7 +573,7 @@ async fn calculate_fees( | TxOutput::CreateDelegationId(_, _) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }) }) .collect(); @@ -607,7 +607,7 @@ async fn calculate_fees( | TxOutput::IssueFungibleToken(_) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }, TxInput::Account(_) => None, TxInput::AccountCommand(_, cmd) => match cmd { @@ -782,7 +782,7 @@ async fn prefetch_pool_data( | TxOutput::IssueNft(_, _, _) | TxOutput::IssueFungibleToken(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_), + | TxOutput::AnyoneCanTake(_), ) => {} None => {} } @@ -1163,7 +1163,7 @@ async fn update_tables_from_transaction_inputs( | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => {} + | TxOutput::AnyoneCanTake(_) => {} TxOutput::CreateStakePool(pool_id, _) | TxOutput::ProduceBlockFromStake(_, pool_id) => { let pool_data = db_tx @@ -1217,7 +1217,7 @@ async fn update_tables_from_transaction_inputs( | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) | TxOutput::IssueFungibleToken(_) - | TxOutput::CreateOrder(_) => {} + | TxOutput::AnyoneCanTake(_) => {} TxOutput::CreateStakePool(pool_id, _) | TxOutput::ProduceBlockFromStake(_, pool_id) => { let pool_data = db_tx @@ -1673,7 +1673,7 @@ async fn update_tables_from_transaction_outputs( } } TxOutput::Htlc(_, _) => {} // TODO(HTLC) - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), } } @@ -1873,7 +1873,7 @@ fn get_tx_output_destination(txo: &TxOutput) -> Option<&Destination> { | TxOutput::Burn(_) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::Htlc(_, _) => None, // TODO(HTLC) } } diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index 0a13f16a3b..e91dbc2f6c 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -265,7 +265,7 @@ async fn simulation( | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::IssueNft(_, _, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }); staking_pools.extend(new_pools); @@ -285,7 +285,7 @@ async fn simulation( | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::IssueNft(_, _, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }); delegations.extend(new_delegations); @@ -302,7 +302,7 @@ async fn simulation( | TxOutput::CreateDelegationId(_, _) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }); token_ids.extend(new_tokens); @@ -347,7 +347,7 @@ async fn simulation( | TxOutput::LockThenTransfer(_, _, _) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::Htlc(_, _) => {} - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), }); tx.inputs().iter().for_each(|inp| match inp { diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index 5f65f0cb7f..cddcc9dfd7 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -197,7 +197,7 @@ pub fn txoutput_to_json( }, }) } - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), } } diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 0275a52e16..9b750dfc99 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -150,7 +150,7 @@ impl ConstrainedValueAccumulator { | TxOutput::IssueFungibleToken(..) | TxOutput::Burn(..) | TxOutput::DataDeposit(..) - | TxOutput::CreateOrder(..) => { + | TxOutput::AnyoneCanTake(..) => { return Err(Error::SpendingNonSpendableOutput(outpoint.clone())); } TxOutput::IssueNft(token_id, _, _) => { @@ -376,7 +376,7 @@ impl ConstrainedValueAccumulator { CoinOrTokenId::Coin, chain_config.nft_issuance_fee(block_height), )?, - TxOutput::CreateOrder(order_data) => { + TxOutput::AnyoneCanTake(order_data) => { let id = CoinOrTokenId::from_output_value(order_data.give()).unwrap(); insert_or_increase( &mut accumulator.unconstrained_value, diff --git a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs index f1a0d2453f..2e7108f540 100644 --- a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs +++ b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs @@ -66,7 +66,7 @@ fn create_order_constraints(#[case] seed: Seed) { Destination::AnyoneCanSpend, ))]; - let outputs = vec![TxOutput::CreateOrder(order_data.clone())]; + let outputs = vec![TxOutput::AnyoneCanTake(order_data.clone())]; let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, @@ -101,7 +101,7 @@ fn create_order_constraints(#[case] seed: Seed) { Destination::AnyoneCanSpend, ))]; - let outputs = vec![TxOutput::CreateOrder(order_data.clone())]; + let outputs = vec![TxOutput::AnyoneCanTake(order_data.clone())]; let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, @@ -137,7 +137,7 @@ fn create_order_constraints(#[case] seed: Seed) { ))]; let outputs = vec![ - TxOutput::CreateOrder(order_data.clone()), + TxOutput::AnyoneCanTake(order_data.clone()), TxOutput::Transfer( OutputValue::Coin(Amount::from_atoms(1)), Destination::AnyoneCanSpend, @@ -178,7 +178,7 @@ fn create_order_constraints(#[case] seed: Seed) { ))]; let outputs = vec![ - TxOutput::CreateOrder(order_data.clone()), + TxOutput::AnyoneCanTake(order_data.clone()), TxOutput::Transfer( OutputValue::TokenV1(token_id, Amount::from_atoms(1)), Destination::AnyoneCanSpend, @@ -219,7 +219,7 @@ fn create_order_constraints(#[case] seed: Seed) { Destination::AnyoneCanSpend, ))]; - let outputs = vec![TxOutput::CreateOrder(order_data)]; + let outputs = vec![TxOutput::AnyoneCanTake(order_data)]; let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, diff --git a/chainstate/src/detail/chainstateref/epoch_seal.rs b/chainstate/src/detail/chainstateref/epoch_seal.rs index c8306b4083..1ee2c7d353 100644 --- a/chainstate/src/detail/chainstateref/epoch_seal.rs +++ b/chainstate/src/detail/chainstateref/epoch_seal.rs @@ -176,7 +176,7 @@ where | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => { + | TxOutput::AnyoneCanTake(_) => { return Err(EpochSealError::SpendStakeError( SpendStakeError::InvalidBlockRewardOutputType, )); diff --git a/chainstate/src/detail/chainstateref/mod.rs b/chainstate/src/detail/chainstateref/mod.rs index 20770f389d..48224f1873 100644 --- a/chainstate/src/detail/chainstateref/mod.rs +++ b/chainstate/src/detail/chainstateref/mod.rs @@ -699,7 +699,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => Err( + | TxOutput::AnyoneCanTake(_) => Err( CheckBlockError::InvalidBlockRewardOutputType(block.get_id()), ), }, @@ -717,7 +717,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => Err( + | TxOutput::AnyoneCanTake(_) => Err( CheckBlockError::InvalidBlockRewardOutputType(block.get_id()), ), } diff --git a/chainstate/src/detail/mod.rs b/chainstate/src/detail/mod.rs index 87d8ef9530..5790d06df9 100644 --- a/chainstate/src/detail/mod.rs +++ b/chainstate/src/detail/mod.rs @@ -725,7 +725,7 @@ impl Chainstate | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => { /* do nothing */ } + | TxOutput::AnyoneCanTake(_) => { /* do nothing */ } | TxOutput::CreateStakePool(pool_id, data) => { let _ = db .create_pool(*pool_id, data.as_ref().clone().into()) diff --git a/chainstate/src/detail/query.rs b/chainstate/src/detail/query.rs index 93e369d965..a88367ed3f 100644 --- a/chainstate/src/detail/query.rs +++ b/chainstate/src/detail/query.rs @@ -351,7 +351,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::IssueNft(_, issuance, _) => match issuance.as_ref() { NftIssuance::V0(nft) => { Some(RPCTokenInfo::new_nonfungible(RPCNonFungibleTokenInfo::new( diff --git a/chainstate/src/interface/chainstate_interface_impl.rs b/chainstate/src/interface/chainstate_interface_impl.rs index 50f290a854..0823ec8bbf 100644 --- a/chainstate/src/interface/chainstate_interface_impl.rs +++ b/chainstate/src/interface/chainstate_interface_impl.rs @@ -832,7 +832,7 @@ fn get_output_coin_amount( | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }; Ok(amount) diff --git a/chainstate/src/rpc/types/output.rs b/chainstate/src/rpc/types/output.rs index 1b1dd15423..45b766e087 100644 --- a/chainstate/src/rpc/types/output.rs +++ b/chainstate/src/rpc/types/output.rs @@ -151,7 +151,7 @@ pub enum RpcTxOutput { value: RpcOutputValue, htlc: RpcHashedTimelockContract, }, - CreateOrder { + AnyoneCanTake { authority: RpcAddress, ask_value: RpcOutputValue, give_value: RpcOutputValue, @@ -208,8 +208,8 @@ impl RpcTxOutput { TxOutput::DataDeposit(data) => RpcTxOutput::DataDeposit { data: RpcHexString::from_bytes(data), }, - TxOutput::CreateOrder(data) => RpcTxOutput::CreateOrder { - authority: RpcAddress::new(chain_config, data.authority().clone())?, + TxOutput::AnyoneCanTake(data) => RpcTxOutput::AnyoneCanTake { + authority: RpcAddress::new(chain_config, data.withdraw_key().clone())?, ask_value: RpcOutputValue::new(chain_config, data.ask().clone())?, give_value: RpcOutputValue::new(chain_config, data.give().clone())?, }, diff --git a/chainstate/test-framework/src/key_manager.rs b/chainstate/test-framework/src/key_manager.rs index 5c2b559d4f..7e6392d721 100644 --- a/chainstate/test-framework/src/key_manager.rs +++ b/chainstate/test-framework/src/key_manager.rs @@ -254,7 +254,7 @@ fn is_htlc_output(output: &TxOutput) -> bool { | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => false, + | TxOutput::AnyoneCanTake(_) => false, TxOutput::Htlc(_, _) => true, } } diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index e80fecc966..2efb62b182 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -281,7 +281,7 @@ impl<'a> RandomTxMaker<'a> { | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => { /* do nothing */ } + | TxOutput::AnyoneCanTake(_) => { /* do nothing */ } TxOutput::CreateStakePool(pool_id, _) => { let (staker_sk, vrf_sk) = new_staking_pools.get(pool_id).unwrap(); staking_pools_observer.on_pool_created( @@ -687,7 +687,7 @@ impl<'a> RandomTxMaker<'a> { | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => unreachable!(), + | TxOutput::AnyoneCanTake(_) => unreachable!(), }; result_inputs.extend(new_inputs); @@ -1050,7 +1050,7 @@ impl<'a> RandomTxMaker<'a> { *dummy_token_id = make_token_id(inputs).unwrap(); Some(output) } - TxOutput::CreateOrder(_) => unimplemented!(), + TxOutput::AnyoneCanTake(_) => unimplemented!(), }) .collect(); diff --git a/chainstate/test-framework/src/signature_destination_getter.rs b/chainstate/test-framework/src/signature_destination_getter.rs index 6337fd120d..1c5041e273 100644 --- a/chainstate/test-framework/src/signature_destination_getter.rs +++ b/chainstate/test-framework/src/signature_destination_getter.rs @@ -77,7 +77,7 @@ impl<'a> SignatureDestinationGetter<'a> { TxOutput::CreateDelegationId(_, _) | TxOutput::Burn(_) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => { + | TxOutput::AnyoneCanTake(_) => { // This error is emitted in other places for attempting to make this spend, // but this is just a double-check. Err(SignatureDestinationGetterError::SigVerifyOfNotSpendableOutput) @@ -179,7 +179,7 @@ impl<'a> SignatureDestinationGetter<'a> { .ok_or(SignatureDestinationGetterError::OrderDataNotFound( *order_id, ))?; - Ok(order_data.authority().clone()) + Ok(order_data.withdraw_key().clone()) } AccountCommand::FillOrder(_, _, d) => Ok(d.clone()), }, diff --git a/chainstate/test-framework/src/utils.rs b/chainstate/test-framework/src/utils.rs index 13aac4dd80..122c6ea296 100644 --- a/chainstate/test-framework/src/utils.rs +++ b/chainstate/test-framework/src/utils.rs @@ -69,7 +69,7 @@ pub fn get_output_value(output: &TxOutput) -> Option { | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::IssueNft(token_id, _, _) => { Some(OutputValue::TokenV1(*token_id, Amount::from_atoms(1))) } @@ -131,7 +131,7 @@ pub fn create_utxo_data( | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, } } @@ -389,7 +389,7 @@ pub fn find_create_pool_tx_in_genesis(genesis: &Genesis, pool_id: &PoolId) -> Op | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => false, + | TxOutput::AnyoneCanTake(_) => false, TxOutput::CreateStakePool(genesis_pool_id, _) => genesis_pool_id == pool_id, }); diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index 4e8d50f33d..8f8875cd04 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -99,7 +99,7 @@ fn create_order_check_storage(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::CreateOrder(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -139,8 +139,8 @@ fn create_two_orders_same_tx(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::CreateOrder(order_data.clone())) - .add_output(TxOutput::CreateOrder(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) .build(); let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); @@ -178,7 +178,7 @@ fn withdraw_order_check_storage(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::CreateOrder(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -230,7 +230,7 @@ fn fill_order_check_storage(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::CreateOrder(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -341,7 +341,7 @@ fn withdraw_order_check_signature(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::CreateOrder(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -489,7 +489,7 @@ fn reorg_before_create(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::CreateOrder(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -576,7 +576,7 @@ fn reorg_after_create(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::CreateOrder(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) // transfer output just to be able to spend something in alternative branch .add_output(TxOutput::Transfer( OutputValue::TokenV1(token_id, Amount::from_atoms(100)), diff --git a/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs b/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs index 73fefd444d..1a346c7602 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs @@ -156,7 +156,7 @@ fn check_tokens_tx( OutputValue::Coin(_) | OutputValue::TokenV1(_, _) => false, OutputValue::TokenV0(_) => true, }, - TxOutput::CreateOrder(data) => { + TxOutput::AnyoneCanTake(data) => { let ask_token_v0 = match data.ask() { OutputValue::Coin(_) | OutputValue::TokenV1(_, _) => false, OutputValue::TokenV0(_) => true, @@ -213,7 +213,7 @@ fn check_tokens_tx( | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => Ok(()), + | TxOutput::AnyoneCanTake(_) => Ok(()), }) .map_err(CheckTransactionError::TokensError)?; @@ -267,7 +267,7 @@ fn check_data_deposit_outputs( | TxOutput::IssueFungibleToken(..) | TxOutput::IssueNft(..) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(..) => { /* Do nothing */ } + | TxOutput::AnyoneCanTake(..) => { /* Do nothing */ } TxOutput::DataDeposit(v) => { // Ensure the size of the data doesn't exceed the max allowed if v.len() > chain_config.data_deposit_max_size() { @@ -310,7 +310,7 @@ fn check_htlc_outputs( | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => false, + | TxOutput::AnyoneCanTake(_) => false, TxOutput::Htlc(_, _) => true, }); diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs index 380b2f14b1..1e7a14a7e2 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/mod.rs @@ -76,7 +76,7 @@ pub fn calculate_tokens_burned_in_outputs( | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }) .sum::>() .ok_or(ConnectTransactionError::BurnAmountSumError(tx.get_id())) @@ -231,7 +231,7 @@ fn check_issuance_fee_burn_v0( | TxOutput::DataDeposit(_) | TxOutput::DelegateStaking(_, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }) .sum::>() .ok_or_else(|| ConnectTransactionError::BurnAmountSumError(tx.get_id()))?; diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs index 2040d04aa5..e3d96eb258 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/purposes_check.rs @@ -65,7 +65,7 @@ pub fn check_reward_inputs_outputs_purposes( | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) | TxOutput::Htlc(..) - | TxOutput::CreateOrder(..) => Err(ConnectTransactionError::IOPolicyError( + | TxOutput::AnyoneCanTake(..) => Err(ConnectTransactionError::IOPolicyError( IOPolicyError::InvalidInputTypeInReward, block_id.into(), )), @@ -90,7 +90,7 @@ pub fn check_reward_inputs_outputs_purposes( | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) | TxOutput::Htlc(..) - | TxOutput::CreateOrder(..) => { + | TxOutput::AnyoneCanTake(..) => { Err(ConnectTransactionError::IOPolicyError( IOPolicyError::InvalidOutputTypeInReward, block_id.into(), @@ -142,7 +142,7 @@ pub fn check_reward_inputs_outputs_purposes( | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) | TxOutput::Htlc(..) - | TxOutput::CreateOrder(..) => false, + | TxOutput::AnyoneCanTake(..) => false, }); ensure!( all_lock_then_transfer, @@ -174,7 +174,7 @@ pub fn check_tx_inputs_outputs_purposes( | TxOutput::DelegateStaking(..) | TxOutput::IssueFungibleToken(..) | TxOutput::DataDeposit(..) - | TxOutput::CreateOrder(..) => false, + | TxOutput::AnyoneCanTake(..) => false, }); ensure!(are_inputs_valid, IOPolicyError::InvalidInputTypeInTx); @@ -207,7 +207,7 @@ pub fn check_tx_inputs_outputs_purposes( | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) | TxOutput::Htlc(..) - | TxOutput::CreateOrder(..) => { /* do nothing */ } + | TxOutput::AnyoneCanTake(..) => { /* do nothing */ } TxOutput::CreateStakePool(..) => { stake_pool_outputs_count += 1; } diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs index b65085066b..31d8fe7fdf 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs @@ -47,7 +47,7 @@ fn update_functions_below_if_new_outputs_were_added(output: TxOutput) { TxOutput::IssueNft(_, _, _) => unimplemented!(), TxOutput::DataDeposit(_) => unimplemented!(), TxOutput::Htlc(_, _) => unimplemented!(), - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => unimplemented!(), } } @@ -238,7 +238,7 @@ pub fn data_deposit() -> TxOutput { } pub fn create_order() -> TxOutput { - TxOutput::CreateOrder(OrderData::new( + TxOutput::AnyoneCanTake(OrderData::new( Destination::AnyoneCanSpend, OutputValue::Coin(Amount::ZERO), OutputValue::Coin(Amount::ZERO), @@ -276,7 +276,7 @@ pub fn is_stake_pool(output: &TxOutput) -> bool { | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) | TxOutput::Htlc(..) - | TxOutput::CreateOrder(..) => false, + | TxOutput::AnyoneCanTake(..) => false, TxOutput::CreateStakePool(..) => true, } } @@ -293,7 +293,7 @@ pub fn is_produce_block(output: &TxOutput) -> bool { | TxOutput::IssueNft(..) | TxOutput::DataDeposit(..) | TxOutput::Htlc(..) - | TxOutput::CreateOrder(..) => false, + | TxOutput::AnyoneCanTake(..) => false, TxOutput::ProduceBlockFromStake(..) => true, } } diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 1133102d11..6f4073501b 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -327,7 +327,7 @@ where | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => Ok(None), + | TxOutput::AnyoneCanTake(_) => Ok(None), } } @@ -448,7 +448,7 @@ where | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }) .collect::, _>>()?; @@ -629,7 +629,7 @@ where | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::IssueFungibleToken(issuance_data) => { let result = make_token_id(tx.inputs()) .ok_or(ConnectTransactionError::TokensError( @@ -702,7 +702,7 @@ where | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => Ok(()), + | TxOutput::AnyoneCanTake(_) => Ok(()), } }) } @@ -791,9 +791,9 @@ where | TxOutput::DataDeposit(..) | TxOutput::IssueFungibleToken(..) | TxOutput::Htlc(_, _) => None, - | TxOutput::CreateOrder(order_data) => match input_utxo_outpoint { + TxOutput::AnyoneCanTake(order_data) => match input_utxo_outpoint { Some(input_utxo_outpoint) => { - let order_id = make_order_id(&input_utxo_outpoint); + let order_id = make_order_id(input_utxo_outpoint); let result = self .orders_accounting_cache .create_order(order_id, order_data.clone()) diff --git a/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs b/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs index 8a4fa239df..cda00723db 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs @@ -241,7 +241,7 @@ fn has_tokens_issuance_to_cache(outputs: &[TxOutput]) -> Option { | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::IssueNft(id, _, _) => Some(*id), }) } diff --git a/common/src/chain/order.rs b/common/src/chain/order.rs index 5fa50b8ec9..c9d202e90b 100644 --- a/common/src/chain/order.rs +++ b/common/src/chain/order.rs @@ -80,22 +80,22 @@ pub fn make_order_id(input0_outpoint: &UtxoOutPoint) -> OrderId { #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] pub struct OrderData { - authority: Destination, + withdraw_key: Destination, ask: OutputValue, give: OutputValue, } impl OrderData { - pub fn new(authority: Destination, ask: OutputValue, give: OutputValue) -> Self { + pub fn new(withdraw_key: Destination, ask: OutputValue, give: OutputValue) -> Self { Self { - authority, + withdraw_key, ask, give, } } - pub fn authority(&self) -> &Destination { - &self.authority + pub fn withdraw_key(&self) -> &Destination { + &self.withdraw_key } pub fn ask(&self) -> &OutputValue { diff --git a/common/src/chain/tokens/tokens_utils.rs b/common/src/chain/tokens/tokens_utils.rs index ae779b76c3..69b963ea53 100644 --- a/common/src/chain/tokens/tokens_utils.rs +++ b/common/src/chain/tokens/tokens_utils.rs @@ -41,7 +41,7 @@ pub fn get_issuance_count_via_tokens_op(outputs: &[TxOutput]) -> usize { | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => false, + | TxOutput::AnyoneCanTake(_) => false, TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) => true, }) .count() @@ -83,7 +83,7 @@ pub fn is_token_or_nft_issuance(output: &TxOutput) -> bool { | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => false, + | TxOutput::AnyoneCanTake(_) => false, TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) => true, } } diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs index 21c00f1520..7761dcde30 100644 --- a/common/src/chain/transaction/output/mod.rs +++ b/common/src/chain/transaction/output/mod.rs @@ -142,7 +142,7 @@ pub enum TxOutput { #[codec(index = 10)] Htlc(OutputValue, Box), #[codec(index = 11)] - CreateOrder(OrderData), + AnyoneCanTake(OrderData), } impl TxOutput { @@ -158,7 +158,7 @@ impl TxOutput { | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::LockThenTransfer(_, _, tl) => Some(tl), } } @@ -330,9 +330,9 @@ impl TextSummary for TxOutput { fmt_dest(&htlc.refund_key) ) } - TxOutput::CreateOrder(order) => format!( - "CreateOrder(Authority({}), AskValue({}), GiveValue({}))", - fmt_dest(order.authority()), + TxOutput::AnyoneCanTake(order) => format!( + "AnyoneCanTake(WithdrawKey({}), AskValue({}), GiveValue({}))", + fmt_dest(order.withdraw_key()), fmt_val(order.ask()), fmt_val(order.give()), ), diff --git a/common/src/chain/transaction/signature/tests/mod.rs b/common/src/chain/transaction/signature/tests/mod.rs index bfc108c496..9d4cd4c555 100644 --- a/common/src/chain/transaction/signature/tests/mod.rs +++ b/common/src/chain/transaction/signature/tests/mod.rs @@ -768,7 +768,7 @@ fn check_mutate_output( TxOutput::IssueNft(_, _, _) => unreachable!(), TxOutput::DataDeposit(_) => unreachable!(), TxOutput::Htlc(_, _) => unreachable!(), - TxOutput::CreateOrder(_) => unreachable!(), + TxOutput::AnyoneCanTake(_) => unreachable!(), }; let tx = tx_updater.generate_tx().unwrap(); diff --git a/common/src/chain/transaction/signature/tests/sign_and_mutate.rs b/common/src/chain/transaction/signature/tests/sign_and_mutate.rs index 03ddc9cdb8..7e9b354344 100644 --- a/common/src/chain/transaction/signature/tests/sign_and_mutate.rs +++ b/common/src/chain/transaction/signature/tests/sign_and_mutate.rs @@ -1138,7 +1138,7 @@ fn mutate_output(_rng: &mut impl Rng, tx: &SignedTransactionWithUtxo) -> SignedT TxOutput::IssueNft(_, _, _) => unreachable!(), // TODO: come back to this later TxOutput::DataDeposit(_) => unreachable!(), TxOutput::Htlc(_, _) => unreachable!(), - TxOutput::CreateOrder(_) => unreachable!(), + TxOutput::AnyoneCanTake(_) => unreachable!(), }; SignedTransactionWithUtxo { tx: updater.generate_tx().unwrap(), diff --git a/common/src/size_estimation/mod.rs b/common/src/size_estimation/mod.rs index 7d68400dc1..8645649619 100644 --- a/common/src/size_estimation/mod.rs +++ b/common/src/size_estimation/mod.rs @@ -110,6 +110,6 @@ fn get_tx_output_destination(txo: &TxOutput) -> Option<&Destination> { | TxOutput::Burn(_) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, } } diff --git a/consensus/src/pos/block_sig.rs b/consensus/src/pos/block_sig.rs index 7138707b2d..ec24bf3971 100644 --- a/consensus/src/pos/block_sig.rs +++ b/consensus/src/pos/block_sig.rs @@ -52,7 +52,7 @@ fn get_staking_kernel_destination( | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => { + | TxOutput::AnyoneCanTake(_) => { return Err(BlockSignatureError::WrongOutputType(header.get_id())) } TxOutput::CreateStakePool(_, stake_pool) => stake_pool.staker(), diff --git a/consensus/src/pos/mod.rs b/consensus/src/pos/mod.rs index fc3216b8c0..f14e8a9179 100644 --- a/consensus/src/pos/mod.rs +++ b/consensus/src/pos/mod.rs @@ -166,7 +166,7 @@ where | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => { + | TxOutput::AnyoneCanTake(_) => { // only pool outputs can be staked return Err(ConsensusPoSError::RandomnessError( PoSRandomnessError::InvalidOutputTypeInStakeKernel(header.get_id()), diff --git a/mempool/src/pool/tx_pool/store/mem_usage.rs b/mempool/src/pool/tx_pool/store/mem_usage.rs index 2e5bf615f5..5399b53564 100644 --- a/mempool/src/pool/tx_pool/store/mem_usage.rs +++ b/mempool/src/pool/tx_pool/store/mem_usage.rs @@ -348,7 +348,7 @@ impl MemoryUsage for TxOutput { TxOutput::IssueNft(_, issuance, _) => issuance.indirect_memory_usage(), TxOutput::DataDeposit(v) => v.indirect_memory_usage(), TxOutput::Htlc(_, htlc) => htlc.indirect_memory_usage(), - TxOutput::CreateOrder(_) => 0, + TxOutput::AnyoneCanTake(_) => 0, } } } diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index 74311a80d8..923a4c45fd 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -180,7 +180,7 @@ impl TranslateInput for SignedTransaction { TxOutput::IssueFungibleToken(_issuance) => Err(TranslationError::Unspendable), TxOutput::Burn(_val) => Err(TranslationError::Unspendable), TxOutput::DataDeposit(_data) => Err(TranslationError::Unspendable), - TxOutput::CreateOrder(_) => Err(TranslationError::Unspendable), + TxOutput::AnyoneCanTake(_) => Err(TranslationError::Unspendable), }, InputInfo::Account { outpoint } => match outpoint.account() { AccountSpending::DelegationBalance(delegation_id, _amount) => { @@ -224,7 +224,7 @@ impl TranslateInput for BlockRewardTransactable<'_> | TxOutput::LockThenTransfer(_, _, _) | TxOutput::IssueNft(_, _, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => Err(TranslationError::IllegalOutputSpend), + | TxOutput::AnyoneCanTake(_) => Err(TranslationError::IllegalOutputSpend), TxOutput::CreateDelegationId(_, _) | TxOutput::Burn(_) | TxOutput::DataDeposit(_) @@ -282,7 +282,7 @@ impl TranslateInput for TimelockOnly { | TxOutput::CreateStakePool(_, _) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::IssueNft(_, _, _) - | TxOutput::CreateOrder(_) => Ok(WitnessScript::TRUE), + | TxOutput::AnyoneCanTake(_) => Ok(WitnessScript::TRUE), TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DelegateStaking(_, _) diff --git a/utxo/src/cache.rs b/utxo/src/cache.rs index 4e64b30fbc..c8db33e97f 100644 --- a/utxo/src/cache.rs +++ b/utxo/src/cache.rs @@ -505,7 +505,7 @@ fn should_include_in_utxo_set(output: &TxOutput) -> bool { | TxOutput::Burn(..) | TxOutput::IssueFungibleToken(..) | TxOutput::DataDeposit(..) - | TxOutput::CreateOrder(..) => false, + | TxOutput::AnyoneCanTake(..) => false, } } diff --git a/wallet/src/account/currency_grouper/mod.rs b/wallet/src/account/currency_grouper/mod.rs index fbeaf6f663..de53ba4a9d 100644 --- a/wallet/src/account/currency_grouper/mod.rs +++ b/wallet/src/account/currency_grouper/mod.rs @@ -58,7 +58,7 @@ pub(crate) fn group_outputs( get_tx_output(&output).clone(), ))) } - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), }; match output_value { @@ -113,7 +113,7 @@ pub fn group_outputs_with_issuance_fee( get_tx_output(&output).clone(), ))) } - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), }; match output_value { @@ -158,7 +158,7 @@ fn output_spendable_value(output: &TxOutput) -> Result<(Currency, Amount), UtxoS | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => { + | TxOutput::AnyoneCanTake(_) => { return Err(UtxoSelectorError::UnsupportedTransactionOutput(Box::new( output.clone(), ))) diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index 45e95461d8..2c837bece4 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1019,7 +1019,7 @@ impl Account { | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }) .expect("find output with dummy_pool_id"); *old_pool_id = new_pool_id; @@ -1075,7 +1075,7 @@ impl Account { | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::IssueNft(token_id, _, _) => { (*token_id == dummy_token_id).then_some(token_id) } @@ -1550,7 +1550,7 @@ impl Account { | TxOutput::Burn(_) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => Vec::new(), + | TxOutput::AnyoneCanTake(_) => Vec::new(), } } @@ -2192,7 +2192,7 @@ fn group_preselected_inputs( output.clone(), ))) } - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), }; update_preselected_inputs(currency, value, *fee)?; } diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index 8570aa0330..041ec72a0b 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -559,7 +559,7 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => false, + | TxOutput::AnyoneCanTake(_) => false, } } @@ -721,7 +721,7 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::ProduceBlockFromStake(_, _) => false, - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), } }), TxInput::AccountCommand(_, cmd) => match cmd { @@ -835,7 +835,7 @@ impl OutputCache { } } TxOutput::IssueNft(_, _, _) => {} - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), }; } Ok(()) @@ -1053,7 +1053,7 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::Htlc(_, _) => {} - TxOutput::CreateOrder(_) => todo!(), + TxOutput::AnyoneCanTake(_) => todo!(), } } } @@ -1379,7 +1379,7 @@ impl OutputCache { | TxOutput::Transfer(_, _) | TxOutput::LockThenTransfer(_, _, _) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::ProduceBlockFromStake(_, pool_id) | TxOutput::CreateStakePool(pool_id, _) => { self.pools.get(pool_id).and_then(|pool_data| { @@ -1423,7 +1423,7 @@ fn is_v0_token_output(output: &TxOutput) -> bool { | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) | TxOutput::ProduceBlockFromStake(_, _) - | TxOutput::CreateOrder(_) => false, + | TxOutput::AnyoneCanTake(_) => false, } } diff --git a/wallet/src/account/transaction_list/mod.rs b/wallet/src/account/transaction_list/mod.rs index b2b277ea3e..0b46e91f8c 100644 --- a/wallet/src/account/transaction_list/mod.rs +++ b/wallet/src/account/transaction_list/mod.rs @@ -122,7 +122,7 @@ fn own_output(key_chain: &AccountKeyChainImpl, output: &TxOutput) -> bool { | TxOutput::IssueFungibleToken(_) | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => false, + | TxOutput::AnyoneCanTake(_) => false, } } diff --git a/wallet/src/account/utxo_selector/output_group.rs b/wallet/src/account/utxo_selector/output_group.rs index 602969e74b..061c5c0bd5 100644 --- a/wallet/src/account/utxo_selector/output_group.rs +++ b/wallet/src/account/utxo_selector/output_group.rs @@ -68,7 +68,7 @@ impl OutputGroup { | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => { + | TxOutput::AnyoneCanTake(_) => { return Err(UtxoSelectorError::UnsupportedTransactionOutput(Box::new( output.1.clone(), ))) diff --git a/wallet/src/send_request/mod.rs b/wallet/src/send_request/mod.rs index da591d7a75..2ec6174ab9 100644 --- a/wallet/src/send_request/mod.rs +++ b/wallet/src/send_request/mod.rs @@ -307,7 +307,7 @@ where | TxOutput::Burn(_) | TxOutput::DelegateStaking(_, _) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::Htlc(_, _) => None, // TODO(HTLC) } } diff --git a/wallet/types/src/utxo_types.rs b/wallet/types/src/utxo_types.rs index 48fe48aa76..47ffa3d548 100644 --- a/wallet/types/src/utxo_types.rs +++ b/wallet/types/src/utxo_types.rs @@ -53,7 +53,7 @@ pub fn get_utxo_type(output: &TxOutput) -> Option { | TxOutput::DelegateStaking(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, TxOutput::Htlc(_, _) => None, // TODO(HTLC) } } diff --git a/wallet/wallet-controller/src/lib.rs b/wallet/wallet-controller/src/lib.rs index 4e123df651..0926f49ff6 100644 --- a/wallet/wallet-controller/src/lib.rs +++ b/wallet/wallet-controller/src/lib.rs @@ -627,7 +627,7 @@ impl Controll | TxOutput::IssueNft(_, _, _) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) - | TxOutput::CreateOrder(_) => None, + | TxOutput::AnyoneCanTake(_) => None, }); let mut balances = BTreeMap::new(); for pool_id in pool_ids { From ae379594a89eaaa1cc7f03363805d4d08d5d9d13 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 5 May 2024 18:02:32 +0300 Subject: [PATCH 18/37] Stub api-server and wallet --- .../scanner-lib/src/blockchain_state/mod.rs | 14 +++--- .../stack-test-suite/tests/v2/transaction.rs | 4 +- api-server/web-server/src/api/json_helpers.rs | 35 ++++++++++++--- wallet/src/account/currency_grouper/mod.rs | 6 ++- wallet/src/account/mod.rs | 18 +++++--- wallet/src/account/output_cache/mod.rs | 45 +++++++++++-------- 6 files changed, 82 insertions(+), 40 deletions(-) diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 8d2a79c1d7..7fa573789e 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -617,8 +617,7 @@ async fn calculate_fees( | AccountCommand::UnfreezeToken(token_id) | AccountCommand::LockTokenSupply(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) => Some(*token_id), - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => None, }, }) .collect(); @@ -738,7 +737,7 @@ async fn tx_fees( let pools = prefetch_pool_data(&inputs_utxos, db_tx).await?; let pos_accounting_adapter = PoSAccountingAdapterToCheckFees { pools }; - // FIXME: proper impl + // TODO: support orders let orders_store = orders_accounting::InMemoryOrdersAccounting::new(); let orders_db = orders_accounting::OrdersAccountingDB::new(&orders_store); @@ -1107,8 +1106,9 @@ async fn update_tables_from_transaction_inputs( ) .await; } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => { + // TODO: support orders + } }, TxInput::Account(outpoint) => { match outpoint.account() { @@ -1673,7 +1673,9 @@ async fn update_tables_from_transaction_outputs( } } TxOutput::Htlc(_, _) => {} // TODO(HTLC) - TxOutput::AnyoneCanTake(_) => todo!(), + TxOutput::AnyoneCanTake(_) => { + // TODO: support orders + } } } diff --git a/api-server/stack-test-suite/tests/v2/transaction.rs b/api-server/stack-test-suite/tests/v2/transaction.rs index 54caadf31a..60d6a297e0 100644 --- a/api-server/stack-test-suite/tests/v2/transaction.rs +++ b/api-server/stack-test-suite/tests/v2/transaction.rs @@ -194,7 +194,7 @@ async fn multiple_tx_in_same_block(#[case] seed: Seed) { "is_replaceable": transaction.is_replaceable(), "flags": transaction.flags(), "inputs": transaction.inputs().iter().zip(utxos).map(|(inp, utxo)| json!({ - "input": tx_input_to_json(inp, &chain_config), + "input": tx_input_to_json(inp, &chain_config, &TokenDecimals::Single(None)), "utxo": utxo.as_ref().map(|txo| txoutput_to_json(txo, &chain_config, &TokenDecimals::Single(None))), })).collect::>(), "outputs": transaction.outputs() @@ -338,7 +338,7 @@ async fn ok(#[case] seed: Seed) { "is_replaceable": transaction.is_replaceable(), "flags": transaction.flags(), "inputs": transaction.inputs().iter().zip(utxos).map(|(inp, utxo)| json!({ - "input": tx_input_to_json(inp, &chain_config), + "input": tx_input_to_json(inp, &chain_config, &TokenDecimals::Single(None)), "utxo": utxo.as_ref().map(|txo| txoutput_to_json(txo, &chain_config, &TokenDecimals::Single(None))), })).collect::>(), "outputs": transaction.outputs() diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index cddcc9dfd7..de699aeffc 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -197,7 +197,14 @@ pub fn txoutput_to_json( }, }) } - TxOutput::AnyoneCanTake(_) => todo!(), + TxOutput::AnyoneCanTake(data) => { + json!({ + "type": "AnyoneCanTake", + "withdraw_key": Address::new(chain_config, data.withdraw_key().clone()).expect("no error").as_str(), + "ask_value": outputvalue_to_json(data.ask(), chain_config, token_decimals), + "give_value": outputvalue_to_json(data.give(), chain_config, token_decimals), + }) + } } } @@ -247,7 +254,11 @@ pub fn utxo_outpoint_to_json(utxo: &UtxoOutPoint) -> serde_json::Value { } } -pub fn tx_input_to_json(inp: &TxInput, chain_config: &ChainConfig) -> serde_json::Value { +pub fn tx_input_to_json( + inp: &TxInput, + chain_config: &ChainConfig, + token_decimals: &TokenDecimals, +) -> serde_json::Value { match inp { TxInput::Utxo(utxo) => match utxo.source_id() { OutPointSourceId::Transaction(tx_id) => { @@ -334,8 +345,22 @@ pub fn tx_input_to_json(inp: &TxInput, chain_config: &ChainConfig) -> serde_json "nonce": nonce, }) } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + AccountCommand::WithdrawOrder(order_id) => { + json!({ + "input_type": "AccountCommand", + "command": "WithdrawOrder", + "order_id": Address::new(chain_config, *order_id).expect("addressable").to_string(), + }) + } + AccountCommand::FillOrder(order_id, fill, dest) => { + json!({ + "input_type": "AccountCommand", + "command": "FillOrder", + "order_id": Address::new(chain_config, *order_id).expect("addressable").to_string(), + "fill_value": outputvalue_to_json(fill, chain_config, token_decimals), + "destination": Address::new(chain_config, dest.clone()).expect("no error").as_str(), + }) + } }, } } @@ -352,7 +377,7 @@ pub fn tx_to_json( "flags": tx.flags(), "fee": amount_to_json(additional_info.fee, chain_config.coin_decimals()), "inputs": tx.inputs().iter().zip(additional_info.input_utxos.iter()).map(|(inp, utxo)| json!({ - "input": tx_input_to_json(inp, chain_config), + "input": tx_input_to_json(inp, chain_config, &(&additional_info.token_decimals).into()), "utxo": utxo.as_ref().map(|txo| txoutput_to_json(txo, chain_config, &(&additional_info.token_decimals).into())), })).collect::>(), "outputs": tx.outputs() diff --git a/wallet/src/account/currency_grouper/mod.rs b/wallet/src/account/currency_grouper/mod.rs index de53ba4a9d..be7ff06af9 100644 --- a/wallet/src/account/currency_grouper/mod.rs +++ b/wallet/src/account/currency_grouper/mod.rs @@ -58,7 +58,8 @@ pub(crate) fn group_outputs( get_tx_output(&output).clone(), ))) } - TxOutput::AnyoneCanTake(_) => todo!(), + // TODO: support orders + TxOutput::AnyoneCanTake(_) => unimplemented!(), }; match output_value { @@ -113,7 +114,8 @@ pub fn group_outputs_with_issuance_fee( get_tx_output(&output).clone(), ))) } - TxOutput::AnyoneCanTake(_) => todo!(), + // TODO: support orders + TxOutput::AnyoneCanTake(_) => unimplemented!(), }; match output_value { diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index 2c837bece4..af95a6049d 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1347,8 +1347,9 @@ impl Account { .token_data(token_id) .map(|data| (None, Some(data.authority.clone()))) .ok_or(WalletError::UnknownTokenId(*token_id)), - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), } } }) @@ -1827,8 +1828,9 @@ impl Account { self.find_token(token_id).is_ok() || self.is_destination_mine_or_watched(address) } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, }); let relevant_outputs = self.mark_outputs_as_seen(db_tx, tx.outputs())?; @@ -2192,7 +2194,8 @@ fn group_preselected_inputs( output.clone(), ))) } - TxOutput::AnyoneCanTake(_) => todo!(), + // TODO: support orders + TxOutput::AnyoneCanTake(_) => unimplemented!(), }; update_preselected_inputs(currency, value, *fee)?; } @@ -2236,8 +2239,9 @@ fn group_preselected_inputs( .ok_or(WalletError::OutputAmountOverflow)?, )?; } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } } diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index 041ec72a0b..bee63017ed 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -669,10 +669,10 @@ impl OutputCache { | AccountCommand::UnmintTokens(_) | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) - | AccountCommand::UnfreezeToken(_) => None, + | AccountCommand::UnfreezeToken(_) + | AccountCommand::WithdrawOrder(_) + | AccountCommand::FillOrder(_, _, _) => None, AccountCommand::FreezeToken(frozen_token_id, _) => Some(frozen_token_id), - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), }, }); @@ -721,7 +721,8 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::ProduceBlockFromStake(_, _) => false, - TxOutput::AnyoneCanTake(_) => todo!(), + // TODO: support orders + TxOutput::AnyoneCanTake(_) => unimplemented!(), } }), TxInput::AccountCommand(_, cmd) => match cmd { @@ -731,8 +732,9 @@ impl OutputCache { | AccountCommand::UnfreezeToken(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) | AccountCommand::UnmintTokens(token_id) => frozen_token_id == token_id, - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, TxInput::Account(_) => false, }) @@ -835,7 +837,8 @@ impl OutputCache { } } TxOutput::IssueNft(_, _, _) => {} - TxOutput::AnyoneCanTake(_) => todo!(), + // TODO: support orders + TxOutput::AnyoneCanTake(_) => unimplemented!(), }; } Ok(()) @@ -924,8 +927,9 @@ impl OutputCache { self.token_issuance.insert(*token_id, data); } } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } } @@ -1026,8 +1030,9 @@ impl OutputCache { data.unconfirmed_txs.remove(tx_id); } } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } } @@ -1053,7 +1058,8 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::Htlc(_, _) => {} - TxOutput::AnyoneCanTake(_) => todo!(), + // TODO: support orders + TxOutput::AnyoneCanTake(_) => unimplemented!(), } } } @@ -1317,8 +1323,9 @@ impl OutputCache { data.unconfirmed_txs.remove(&tx_id.into()); } } - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } } @@ -1525,8 +1532,9 @@ fn apply_freeze_mutations_from_tx( | AccountCommand::UnmintTokens(_) | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } } @@ -1566,8 +1574,9 @@ fn apply_total_supply_mutations_from_tx( AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} - AccountCommand::WithdrawOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + // TODO: support orders + AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } } From 6493c3d1c39f11fdfefaaece9451c0e9346c876b Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 5 May 2024 18:12:49 +0300 Subject: [PATCH 19/37] Fix clippy --- .../src/constraints_accumulator.rs | 12 +++++------ .../src/transaction_verifier/mod.rs | 20 +++++++++---------- .../chain/transaction/output/output_value.rs | 2 +- common/src/primitives/mod.rs | 8 ++++---- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 9b750dfc99..4bf94b6952 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -284,12 +284,10 @@ impl ConstrainedValueAccumulator { let ask_amount = (initially_asked - ask_balance) .ok_or(Error::NegativeAccountBalance(AccountType::Order(*id)))?; - let ask_currency = - CoinOrTokenId::from_output_value(order_data.ask()).expect("cannot fail"); + let ask_currency = CoinOrTokenId::from_output_value(order_data.ask()); insert_or_increase(&mut self.unconstrained_value, ask_currency, ask_amount)?; - let give_currency = - CoinOrTokenId::from_output_value(order_data.give()).expect("cannot fail"); + let give_currency = CoinOrTokenId::from_output_value(order_data.give()); insert_or_increase(&mut self.unconstrained_value, give_currency, give_balance)?; Ok((CoinOrTokenId::Coin, Amount::ZERO)) } @@ -304,10 +302,10 @@ impl ConstrainedValueAccumulator { .get_order_data(order_id) .map_err(|_| orders_accounting::Error::ViewFail)? .ok_or(orders_accounting::Error::OrderDataNotFound(*order_id))?; - let give_currency = CoinOrTokenId::from_output_value(order_data.give()).unwrap(); + let give_currency = CoinOrTokenId::from_output_value(order_data.give()); insert_or_increase(&mut self.unconstrained_value, give_currency, filled_amount)?; - let ask_currency = CoinOrTokenId::from_output_value(fill_value).unwrap(); + let ask_currency = CoinOrTokenId::from_output_value(fill_value); Ok((ask_currency, fill_value.amount())) } } @@ -377,7 +375,7 @@ impl ConstrainedValueAccumulator { chain_config.nft_issuance_fee(block_height), )?, TxOutput::AnyoneCanTake(order_data) => { - let id = CoinOrTokenId::from_output_value(order_data.give()).unwrap(); + let id = CoinOrTokenId::from_output_value(order_data.give()); insert_or_increase( &mut accumulator.unconstrained_value, id, diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 6f4073501b..3c4654b2ba 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -203,6 +203,15 @@ where } } +type DerivedTxVerifier<'a, C, S, U, A, T, O> = TransactionVerifier< + &'a ChainConfig, + &'a TransactionVerifier, + &'a UtxosCache, + &'a PoSAccountingDelta, + &'a TokensAccountingCache, + &'a OrdersAccountingCache, +>; + impl TransactionVerifier where C: AsRef, @@ -213,16 +222,7 @@ where O: OrdersAccountingView, ::Error: From, { - pub fn derive_child( - &self, - ) -> TransactionVerifier< - &ChainConfig, - &Self, - &UtxosCache, - &PoSAccountingDelta, - &TokensAccountingCache, - &OrdersAccountingCache, - > { + pub fn derive_child(&self) -> DerivedTxVerifier { TransactionVerifier { storage: self, chain_config: self.chain_config.as_ref(), diff --git a/common/src/chain/transaction/output/output_value.rs b/common/src/chain/transaction/output/output_value.rs index be1b6fb562..ea1b51384d 100644 --- a/common/src/chain/transaction/output/output_value.rs +++ b/common/src/chain/transaction/output/output_value.rs @@ -52,7 +52,7 @@ impl OutputValue { pub fn amount(&self) -> Amount { match self { OutputValue::Coin(v) | OutputValue::TokenV1(_, v) => *v, - OutputValue::TokenV0(_) => unreachable!(), + OutputValue::TokenV0(_) => panic!("deprecated token version"), } } } diff --git a/common/src/primitives/mod.rs b/common/src/primitives/mod.rs index 9226778544..9db28b6f17 100644 --- a/common/src/primitives/mod.rs +++ b/common/src/primitives/mod.rs @@ -51,11 +51,11 @@ pub enum CoinOrTokenId { } impl CoinOrTokenId { - pub fn from_output_value(value: &OutputValue) -> Option { + pub fn from_output_value(value: &OutputValue) -> Self { match value { - OutputValue::Coin(_) => Some(Self::Coin), - OutputValue::TokenV0(_) => None, - OutputValue::TokenV1(id, _) => Some(Self::TokenId(*id)), + OutputValue::Coin(_) => Self::Coin, + OutputValue::TokenV0(_) => panic!("deprecated token version"), + OutputValue::TokenV1(id, _) => Self::TokenId(*id), } } } From 036d37a45c422fb46ed7c77891e66b6bc9a83aaa Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 7 May 2024 14:47:16 +0300 Subject: [PATCH 20/37] Check for activation and same currencies --- chainstate/src/detail/ban_score.rs | 2 + chainstate/src/detail/error_classification.rs | 6 +- .../src/tests/chainstate_storage_tests.rs | 9 +- .../test-suite/src/tests/fungible_tokens.rs | 8 +- chainstate/test-suite/src/tests/htlc.rs | 7 +- chainstate/test-suite/src/tests/nft_burn.rs | 5 +- .../test-suite/src/tests/nft_issuance.rs | 4 +- .../test-suite/src/tests/nft_transfer.rs | 6 +- .../test-suite/src/tests/orders_tests.rs | 268 ++++++++++++++++-- chainstate/test-suite/src/tests/tx_fee.rs | 3 +- .../transaction_verifier/check_transaction.rs | 55 +++- common/src/chain/config/builder.rs | 15 +- common/src/chain/config/mod.rs | 7 +- .../src/chain/upgrades/chainstate_upgrade.rs | 13 + common/src/chain/upgrades/mod.rs | 4 +- mempool/src/error/ban_score.rs | 2 + 16 files changed, 372 insertions(+), 42 deletions(-) diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index cee0ed2089..d78b99d0d7 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -359,6 +359,8 @@ impl BanScore for CheckTransactionError { CheckTransactionError::TxSizeTooLarge(_, _, _) => 100, CheckTransactionError::DeprecatedTokenOperationVersion(_, _) => 100, CheckTransactionError::HtlcsAreNotActivated => 100, + CheckTransactionError::OrdersAreNotActivated(_) => 100, + CheckTransactionError::OrdersCurrenciesMustBeDifferent(_) => 100, } } } diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index b05fe20c52..bd0a926a51 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -794,7 +794,11 @@ impl BlockProcessingErrorClassification for CheckTransactionError { | CheckTransactionError::DataDepositMaxSizeExceeded(_, _, _) | CheckTransactionError::TxSizeTooLarge(_, _, _) | CheckTransactionError::DeprecatedTokenOperationVersion(_, _) - | CheckTransactionError::HtlcsAreNotActivated => BlockProcessingErrorClass::BadBlock, + | CheckTransactionError::HtlcsAreNotActivated + | CheckTransactionError::OrdersAreNotActivated(_) + | CheckTransactionError::OrdersCurrenciesMustBeDifferent(_) => { + BlockProcessingErrorClass::BadBlock + } CheckTransactionError::PropertyQueryError(err) => err.classify(), CheckTransactionError::TokensError(err) => err.classify(), diff --git a/chainstate/test-suite/src/tests/chainstate_storage_tests.rs b/chainstate/test-suite/src/tests/chainstate_storage_tests.rs index 72101923cb..683a652cfa 100644 --- a/chainstate/test-suite/src/tests/chainstate_storage_tests.rs +++ b/chainstate/test-suite/src/tests/chainstate_storage_tests.rs @@ -24,9 +24,9 @@ use common::{ chain::{ output_value::OutputValue, tokens::{make_token_id, NftIssuance, TokenAuxiliaryData, TokenIssuanceV0}, - ChainstateUpgrade, Destination, HtlcActivated, NetUpgrades, OutPointSourceId, - RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, Transaction, TxInput, - TxOutput, UtxoOutPoint, + ChainstateUpgrade, Destination, HtlcActivated, NetUpgrades, OrdersActivated, + OutPointSourceId, RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, + Transaction, TxInput, TxOutput, UtxoOutPoint, }, primitives::{Amount, Id, Idable}, }; @@ -120,6 +120,7 @@ fn store_fungible_token_v0(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), @@ -199,6 +200,7 @@ fn store_nft_v0(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), @@ -509,6 +511,7 @@ fn store_aux_data_from_issue_nft(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), diff --git a/chainstate/test-suite/src/tests/fungible_tokens.rs b/chainstate/test-suite/src/tests/fungible_tokens.rs index 7e0ea18085..eb9423fbd6 100644 --- a/chainstate/test-suite/src/tests/fungible_tokens.rs +++ b/chainstate/test-suite/src/tests/fungible_tokens.rs @@ -28,8 +28,8 @@ use common::{ output_value::OutputValue, signature::inputsig::InputWitness, tokens::{make_token_id, TokenData, TokenId}, - ChainstateUpgrade, Destination, HtlcActivated, OutPointSourceId, TokenIssuanceVersion, - TokensFeeVersion, TxInput, TxOutput, + ChainstateUpgrade, Destination, HtlcActivated, OrdersActivated, OutPointSourceId, + TokenIssuanceVersion, TokensFeeVersion, TxInput, TxOutput, }, primitives::{Amount, Idable}, }; @@ -57,6 +57,7 @@ fn make_test_framework_with_v0(rng: &mut (impl Rng + CryptoRng)) -> TestFramewor RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), @@ -961,6 +962,7 @@ fn no_v0_issuance_after_v1(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), @@ -1024,6 +1026,7 @@ fn no_v0_transfer_after_v1(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), ), ( @@ -1033,6 +1036,7 @@ fn no_v0_transfer_after_v1(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), ), ]) diff --git a/chainstate/test-suite/src/tests/htlc.rs b/chainstate/test-suite/src/tests/htlc.rs index 4e3b604260..19c499f532 100644 --- a/chainstate/test-suite/src/tests/htlc.rs +++ b/chainstate/test-suite/src/tests/htlc.rs @@ -39,7 +39,8 @@ use common::{ timelock::OutputTimeLock, tokens::{make_token_id, TokenData, TokenIssuance, TokenTransfer}, AccountCommand, AccountNonce, ChainConfig, ChainstateUpgrade, Destination, HtlcActivated, - RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, TxInput, TxOutput, + OrdersActivated, RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, + TxInput, TxOutput, }, primitives::{Amount, Idable}, }; @@ -594,6 +595,7 @@ fn fork_activation(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::No, + OrdersActivated::No, ), ), ( @@ -603,6 +605,7 @@ fn fork_activation(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::No, ), ), ]) @@ -691,6 +694,7 @@ fn spend_tokens(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), ), ( @@ -700,6 +704,7 @@ fn spend_tokens(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), ), ]) diff --git a/chainstate/test-suite/src/tests/nft_burn.rs b/chainstate/test-suite/src/tests/nft_burn.rs index 57e7135c31..4564f8885a 100644 --- a/chainstate/test-suite/src/tests/nft_burn.rs +++ b/chainstate/test-suite/src/tests/nft_burn.rs @@ -17,8 +17,8 @@ use chainstate::{BlockError, ChainstateError, ConnectTransactionError}; use chainstate_test_framework::{TestFramework, TransactionBuilder}; use common::chain::{ output_value::OutputValue, signature::inputsig::InputWitness, tokens::make_token_id, - ChainstateUpgrade, Destination, HtlcActivated, RewardDistributionVersion, TokenIssuanceVersion, - TokensFeeVersion, TxInput, TxOutput, + ChainstateUpgrade, Destination, HtlcActivated, OrdersActivated, RewardDistributionVersion, + TokenIssuanceVersion, TokensFeeVersion, TxInput, TxOutput, }; use common::chain::{OutPointSourceId, UtxoOutPoint}; use common::primitives::{Amount, BlockHeight, CoinOrTokenId, Idable}; @@ -216,6 +216,7 @@ fn no_v0_issuance_after_v1(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), diff --git a/chainstate/test-suite/src/tests/nft_issuance.rs b/chainstate/test-suite/src/tests/nft_issuance.rs index 3810f9f6c7..8a559d2961 100644 --- a/chainstate/test-suite/src/tests/nft_issuance.rs +++ b/chainstate/test-suite/src/tests/nft_issuance.rs @@ -22,7 +22,7 @@ use common::chain::{ output_value::OutputValue, signature::inputsig::InputWitness, tokens::{is_rfc3986_valid_symbol, make_token_id, Metadata, NftIssuance, NftIssuanceV0}, - Block, ChainstateUpgrade, Destination, HtlcActivated, OutPointSourceId, + Block, ChainstateUpgrade, Destination, HtlcActivated, OrdersActivated, OutPointSourceId, RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, TxInput, TxOutput, }; use common::primitives::{BlockHeight, Idable}; @@ -1652,6 +1652,7 @@ fn no_v0_issuance_after_v1(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), @@ -1715,6 +1716,7 @@ fn only_ascii_alphanumeric_after_v1(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), diff --git a/chainstate/test-suite/src/tests/nft_transfer.rs b/chainstate/test-suite/src/tests/nft_transfer.rs index e0b1b4e9b7..fa8511ea5c 100644 --- a/chainstate/test-suite/src/tests/nft_transfer.rs +++ b/chainstate/test-suite/src/tests/nft_transfer.rs @@ -21,8 +21,9 @@ use common::{ output_value::OutputValue, signature::inputsig::InputWitness, tokens::{make_token_id, NftIssuance, TokenId}, - ChainstateUpgrade, Destination, HtlcActivated, NetUpgrades, OutPointSourceId, - RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, TxInput, TxOutput, + ChainstateUpgrade, Destination, HtlcActivated, NetUpgrades, OrdersActivated, + OutPointSourceId, RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, + TxInput, TxOutput, }, primitives::{Amount, BlockHeight, CoinOrTokenId}, }; @@ -370,6 +371,7 @@ fn ensure_nft_cannot_be_printed_from_tokens_op(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index 8f8875cd04..676d786b2e 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -25,10 +25,10 @@ use common::{ DestinationSigError, }, tokens::{TokenId, TokenIssuance}, - AccountCommand, AccountNonce, Destination, OrderData, SignedTransaction, TxInput, TxOutput, - UtxoOutPoint, + AccountCommand, AccountNonce, ChainstateUpgrade, Destination, OrderData, SignedTransaction, + TxInput, TxOutput, UtxoOutPoint, }, - primitives::{Amount, Idable}, + primitives::{Amount, BlockHeight, Idable}, }; use crypto::key::{KeyKind, PrivateKey}; use orders_accounting::OrdersAccountingDB; @@ -42,23 +42,29 @@ use tx_verifier::error::{InputCheckError, ScriptError}; use crate::tests::helpers::{issue_token_from_block, mint_tokens_in_block}; -fn issue_and_mint_token( +fn issue_and_mint_token_from_genesis( rng: &mut (impl Rng + CryptoRng), tf: &mut TestFramework, ) -> (TokenId, UtxoOutPoint, UtxoOutPoint) { - let genesis_block_id = tf.best_block_id(); + let genesis_block_id = tf.genesis().get_id(); + let utxo = UtxoOutPoint::new(genesis_block_id.into(), 0); + + issue_and_mint_token_from_best_block(rng, tf, utxo) +} + +fn issue_and_mint_token_from_best_block( + rng: &mut (impl Rng + CryptoRng), + tf: &mut TestFramework, + utxo_outpoint: UtxoOutPoint, +) -> (TokenId, UtxoOutPoint, UtxoOutPoint) { + let best_block_id = tf.best_block_id(); let issuance = TokenIssuance::V1(random_token_issuance_v1( tf.chain_config(), Destination::AnyoneCanSpend, rng, )); - let (token_id, _, utxo_with_change) = issue_token_from_block( - rng, - tf, - genesis_block_id, - UtxoOutPoint::new(genesis_block_id.into(), 0), - issuance, - ); + let (token_id, _, utxo_with_change) = + issue_token_from_block(rng, tf, best_block_id, utxo_outpoint, issuance); let best_block_id = tf.best_block_id(); let (_, mint_tx_id) = mint_tokens_in_block( @@ -86,7 +92,7 @@ fn create_order_check_storage(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); - let (token_id, tokens_outpoint, _) = issue_and_mint_token(&mut rng, &mut tf); + let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); @@ -126,7 +132,7 @@ fn create_two_orders_same_tx(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); - let (token_id, tokens_outpoint, _) = issue_and_mint_token(&mut rng, &mut tf); + let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); @@ -157,6 +163,139 @@ fn create_two_orders_same_tx(#[case] seed: Seed) { }); } +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_order_check_currencies(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + + // Check coins for coins trade + { + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::Coin(give_amount), + ); + + let tx = TransactionBuilder::new() + .add_input( + tokens_outpoint.clone().into(), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::AnyoneCanTake(order_data)) + .build(); + let tx_id = tx.transaction().get_id(); + let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + assert_eq!( + result.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::CheckBlockFailed( + chainstate::CheckBlockError::CheckTransactionFailed( + chainstate::CheckBlockTransactionsError::CheckTransactionError( + tx_verifier::CheckTransactionError::OrdersCurrenciesMustBeDifferent( + tx_id + ) + ) + ) + ) + ) + ); + } + + // Check tokens for tokens trade + { + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let tx = TransactionBuilder::new() + .add_input( + tokens_outpoint.clone().into(), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::AnyoneCanTake(order_data)) + .build(); + let tx_id = tx.transaction().get_id(); + let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + assert_eq!( + result.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::CheckBlockFailed( + chainstate::CheckBlockError::CheckTransactionFailed( + chainstate::CheckBlockTransactionsError::CheckTransactionError( + tx_verifier::CheckTransactionError::OrdersCurrenciesMustBeDifferent( + tx_id + ) + ) + ) + ) + ) + ); + } + + // Trade tokens for coins + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + tf.make_block_builder() + .add_transaction( + TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .build(), + ) + .build_and_process(&mut rng) + .unwrap(); + }); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_order_tokens_for_tokens(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id_1, _, coins_outpoint) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); + + let (token_id_2, tokens_outpoint_2, _) = + issue_and_mint_token_from_best_block(&mut rng, &mut tf, coins_outpoint); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + + // Trade tokens for coins + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id_1, ask_amount), + OutputValue::TokenV1(token_id_2, give_amount), + ); + + tf.make_block_builder() + .add_transaction( + TransactionBuilder::new() + .add_input(tokens_outpoint_2.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .build(), + ) + .build_and_process(&mut rng) + .unwrap(); + }); +} + #[rstest] #[trace] #[case(Seed::from_entropy())] @@ -165,7 +304,7 @@ fn withdraw_order_check_storage(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); - let (token_id, tokens_outpoint, _) = issue_and_mint_token(&mut rng, &mut tf); + let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); @@ -217,7 +356,8 @@ fn fill_order_check_storage(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); - let (token_id, tokens_outpoint, coins_outpoint) = issue_and_mint_token(&mut rng, &mut tf); + let (token_id, tokens_outpoint, coins_outpoint) = + issue_and_mint_token_from_genesis(&mut rng, &mut tf); let ask_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); let give_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); @@ -328,7 +468,7 @@ fn withdraw_order_check_signature(#[case] seed: Seed) { let (order_sk, order_pk) = PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr); - let (token_id, tokens_outpoint, _) = issue_and_mint_token(&mut rng, &mut tf); + let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); @@ -475,7 +615,8 @@ fn reorg_before_create(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); - let (token_id, tokens_outpoint, coins_outpoint) = issue_and_mint_token(&mut rng, &mut tf); + let (token_id, tokens_outpoint, coins_outpoint) = + issue_and_mint_token_from_genesis(&mut rng, &mut tf); let reorg_common_ancestor = tf.best_block_id(); let ask_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); @@ -563,7 +704,8 @@ fn reorg_after_create(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); - let (token_id, tokens_outpoint, coins_outpoint) = issue_and_mint_token(&mut rng, &mut tf); + let (token_id, tokens_outpoint, coins_outpoint) = + issue_and_mint_token_from_genesis(&mut rng, &mut tf); let ask_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); let give_amount = Amount::from_atoms(rng.gen_range(10u128..1000)); @@ -668,3 +810,91 @@ fn reorg_after_create(#[case] seed: Seed) { ); }); } + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn test_activation(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = test_utils::random::make_seedable_rng(seed); + // activate orders at height 4 (genesis + issue block + mint block + empty block) + let mut tf = TestFramework::builder(&mut rng) + .with_chain_config( + common::chain::config::Builder::test_chain() + .chainstate_upgrades( + common::chain::NetUpgrades::initialize(vec![ + ( + BlockHeight::zero(), + ChainstateUpgrade::new( + common::chain::TokenIssuanceVersion::V1, + common::chain::RewardDistributionVersion::V1, + common::chain::TokensFeeVersion::V1, + common::chain::HtlcActivated::No, + common::chain::OrdersActivated::No, + ), + ), + ( + BlockHeight::new(4), + ChainstateUpgrade::new( + common::chain::TokenIssuanceVersion::V1, + common::chain::RewardDistributionVersion::V1, + common::chain::TokensFeeVersion::V1, + common::chain::HtlcActivated::No, + common::chain::OrdersActivated::Yes, + ), + ), + ]) + .unwrap(), + ) + .genesis_unittest(Destination::AnyoneCanSpend) + .build(), + ) + .build(); + + let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); + + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(Amount::from_atoms(rng.gen_range(1u128..1000))), + OutputValue::TokenV1(token_id, Amount::from_atoms(rng.gen_range(1u128..1000))), + ); + + // Try to produce order output before activation, check an error + let tx = TransactionBuilder::new() + .add_input( + tokens_outpoint.clone().into(), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .build(); + let tx_id = tx.transaction().get_id(); + let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + + assert_eq!( + result.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::CheckBlockFailed( + chainstate::CheckBlockError::CheckTransactionFailed( + chainstate::CheckBlockTransactionsError::CheckTransactionError( + tx_verifier::CheckTransactionError::OrdersAreNotActivated(tx_id) + ) + ) + ) + ) + ); + + // produce an empty block + tf.make_block_builder().build_and_process(&mut rng).unwrap(); + + // now it should be possible to use order output + tf.make_block_builder() + .add_transaction( + TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .build(), + ) + .build_and_process(&mut rng) + .unwrap(); + }); +} diff --git a/chainstate/test-suite/src/tests/tx_fee.rs b/chainstate/test-suite/src/tests/tx_fee.rs index 5fbdfd2604..45b80da5f5 100644 --- a/chainstate/test-suite/src/tests/tx_fee.rs +++ b/chainstate/test-suite/src/tests/tx_fee.rs @@ -33,7 +33,7 @@ use common::{ make_token_id, IsTokenFreezable, TokenIssuance, TokenIssuanceV0, TokenIssuanceV1, TokenTotalSupply, }, - ChainConfig, ChainstateUpgrade, Destination, HtlcActivated, NetUpgrades, + ChainConfig, ChainstateUpgrade, Destination, HtlcActivated, NetUpgrades, OrdersActivated, TokenIssuanceVersion, TokensFeeVersion, TxInput, TxOutput, UtxoOutPoint, }, primitives::{Amount, Fee, Idable}, @@ -577,6 +577,7 @@ fn issue_fungible_token_v0(#[case] seed: Seed) { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .unwrap(), diff --git a/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs b/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs index 1a346c7602..d16fcfc536 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/check_transaction.rs @@ -24,7 +24,7 @@ use common::{ ChainConfig, HtlcActivated, SignedTransaction, TokenIssuanceVersion, Transaction, TransactionSize, TxOutput, }, - primitives::{BlockHeight, Id, Idable}, + primitives::{BlockHeight, CoinOrTokenId, Id, Idable}, }; use thiserror::Error; use utils::ensure; @@ -58,6 +58,10 @@ pub enum CheckTransactionError { DeprecatedTokenOperationVersion(TokenIssuanceVersion, Id), #[error("Htlcs are not activated yet")] HtlcsAreNotActivated, + #[error("Orders from tx {0} are not yet activated")] + OrdersAreNotActivated(Id), + #[error("Orders currencies from tx {0} are the same")] + OrdersCurrenciesMustBeDifferent(Id), } pub fn check_transaction( @@ -72,6 +76,7 @@ pub fn check_transaction( check_no_signature_size(chain_config, tx)?; check_data_deposit_outputs(chain_config, tx)?; check_htlc_outputs(chain_config, block_height, tx)?; + check_order_outputs(chain_config, block_height, tx)?; Ok(()) } @@ -284,7 +289,6 @@ fn check_data_deposit_outputs( Ok(()) } -// FIXME: orders here as well fn check_htlc_outputs( chain_config: &ChainConfig, block_height: BlockHeight, @@ -319,3 +323,50 @@ fn check_htlc_outputs( }; Ok(()) } + +fn check_order_outputs( + chain_config: &ChainConfig, + block_height: BlockHeight, + tx: &SignedTransaction, +) -> Result<(), CheckTransactionError> { + for output in tx.outputs() { + match output { + TxOutput::Transfer(..) + | TxOutput::LockThenTransfer(..) + | TxOutput::Burn(..) + | TxOutput::CreateStakePool(..) + | TxOutput::ProduceBlockFromStake(..) + | TxOutput::CreateDelegationId(..) + | TxOutput::DelegateStaking(..) + | TxOutput::IssueFungibleToken(..) + | TxOutput::IssueNft(..) + | TxOutput::DataDeposit(..) + | TxOutput::Htlc(..) => { /* Do nothing */ } + TxOutput::AnyoneCanTake(data) => { + let orders_activated = chain_config + .chainstate_upgrades() + .version_at_height(block_height) + .1 + .orders_activated(); + match orders_activated { + common::chain::OrdersActivated::Yes => { + ensure!( + CoinOrTokenId::from_output_value(data.ask()) + != CoinOrTokenId::from_output_value(data.give()), + CheckTransactionError::OrdersCurrenciesMustBeDifferent( + tx.transaction().get_id() + ) + ) + } + common::chain::OrdersActivated::No => { + return Err(CheckTransactionError::OrdersAreNotActivated( + tx.transaction().get_id(), + )) + } + } + } + } + } + + Ok(()) +} diff --git a/common/src/chain/config/builder.rs b/common/src/chain/config/builder.rs index 02b13c6d59..a8ff8014c4 100644 --- a/common/src/chain/config/builder.rs +++ b/common/src/chain/config/builder.rs @@ -29,8 +29,8 @@ use crate::{ pos_initial_difficulty, pow::PoWChainConfigBuilder, ChainstateUpgrade, CoinUnit, ConsensusUpgrade, Destination, GenBlock, Genesis, - HtlcActivated, NetUpgrades, PoSChainConfig, PoSConsensusVersion, PoWChainConfig, - RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, + HtlcActivated, NetUpgrades, OrdersActivated, PoSChainConfig, PoSConsensusVersion, + PoWChainConfig, RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion, }, primitives::{ id::WithId, per_thousand::PerThousand, semver::SemVer, Amount, BlockCount, BlockDistance, @@ -51,8 +51,8 @@ const TESTNET_TOKEN_FORK_HEIGHT: BlockHeight = BlockHeight::new(78440); // and change various tokens fees const TESTNET_STAKER_REWARD_AND_TOKENS_FEE_FORK_HEIGHT: BlockHeight = BlockHeight::new(138244); // The fork, at which txs with htlc and orders outputs become valid -const TESTNET_HTLC_AND_ORDERS_FORK_HEIGHT: BlockHeight = BlockHeight::new(99999999); -const MAINNET_HTLC_AND_ORDERS_FORK_HEIGHT: BlockHeight = BlockHeight::new(99999999); +const TESTNET_HTLC_AND_ORDERS_FORK_HEIGHT: BlockHeight = BlockHeight::new(99_999_999); +const MAINNET_HTLC_AND_ORDERS_FORK_HEIGHT: BlockHeight = BlockHeight::new(99_999_999); impl ChainType { fn default_genesis_init(&self) -> GenesisBlockInit { @@ -167,6 +167,7 @@ impl ChainType { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::No, + OrdersActivated::No, ), ), ( @@ -176,6 +177,7 @@ impl ChainType { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), ), ]; @@ -189,6 +191,7 @@ impl ChainType { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]; NetUpgrades::initialize(upgrades).expect("net upgrades") @@ -202,6 +205,7 @@ impl ChainType { RewardDistributionVersion::V0, TokensFeeVersion::V0, HtlcActivated::No, + OrdersActivated::No, ), ), ( @@ -211,6 +215,7 @@ impl ChainType { RewardDistributionVersion::V0, TokensFeeVersion::V0, HtlcActivated::No, + OrdersActivated::No, ), ), ( @@ -220,6 +225,7 @@ impl ChainType { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::No, + OrdersActivated::No, ), ), ( @@ -229,6 +235,7 @@ impl ChainType { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), ), ]; diff --git a/common/src/chain/config/mod.rs b/common/src/chain/config/mod.rs index 6aaedd80b2..0b948da7ef 100644 --- a/common/src/chain/config/mod.rs +++ b/common/src/chain/config/mod.rs @@ -51,8 +51,10 @@ use self::checkpoints::Checkpoints; use self::emission_schedule::DEFAULT_INITIAL_MINT; use super::output_value::OutputValue; use super::{stakelock::StakePoolData, RequiredConsensus}; -use super::{ChainstateUpgrade, ConsensusUpgrade, HtlcActivated}; -use super::{RewardDistributionVersion, TokenIssuanceVersion, TokensFeeVersion}; +use super::{ + ChainstateUpgrade, ConsensusUpgrade, HtlcActivated, OrdersActivated, RewardDistributionVersion, + TokenIssuanceVersion, TokensFeeVersion, +}; const DEFAULT_MAX_FUTURE_BLOCK_TIME_OFFSET: Duration = Duration::from_secs(120); const DEFAULT_TARGET_BLOCK_SPACING: Duration = Duration::from_secs(120); @@ -875,6 +877,7 @@ pub fn create_unit_test_config_builder() -> Builder { RewardDistributionVersion::V1, TokensFeeVersion::V1, HtlcActivated::Yes, + OrdersActivated::Yes, ), )]) .expect("cannot fail"), diff --git a/common/src/chain/upgrades/chainstate_upgrade.rs b/common/src/chain/upgrades/chainstate_upgrade.rs index fa44312988..479e813948 100644 --- a/common/src/chain/upgrades/chainstate_upgrade.rs +++ b/common/src/chain/upgrades/chainstate_upgrade.rs @@ -45,12 +45,19 @@ pub enum HtlcActivated { No, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] +pub enum OrdersActivated { + Yes, + No, +} + #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct ChainstateUpgrade { token_issuance_version: TokenIssuanceVersion, reward_distribution_version: RewardDistributionVersion, tokens_fee_version: TokensFeeVersion, htlc_activated: HtlcActivated, + orders_activated: OrdersActivated, } impl ChainstateUpgrade { @@ -59,12 +66,14 @@ impl ChainstateUpgrade { reward_distribution_version: RewardDistributionVersion, tokens_fee_version: TokensFeeVersion, htlc_activated: HtlcActivated, + orders_activated: OrdersActivated, ) -> Self { Self { token_issuance_version, reward_distribution_version, tokens_fee_version, htlc_activated, + orders_activated, } } @@ -83,6 +92,10 @@ impl ChainstateUpgrade { pub fn htlc_activated(&self) -> HtlcActivated { self.htlc_activated } + + pub fn orders_activated(&self) -> OrdersActivated { + self.orders_activated + } } impl Activate for ChainstateUpgrade {} diff --git a/common/src/chain/upgrades/mod.rs b/common/src/chain/upgrades/mod.rs index f1feae3efb..229918d79e 100644 --- a/common/src/chain/upgrades/mod.rs +++ b/common/src/chain/upgrades/mod.rs @@ -18,8 +18,8 @@ mod consensus_upgrade; mod netupgrade; pub use chainstate_upgrade::{ - ChainstateUpgrade, HtlcActivated, RewardDistributionVersion, TokenIssuanceVersion, - TokensFeeVersion, + ChainstateUpgrade, HtlcActivated, OrdersActivated, RewardDistributionVersion, + TokenIssuanceVersion, TokensFeeVersion, }; pub use consensus_upgrade::{ConsensusUpgrade, PoSStatus, PoWStatus, RequiredConsensus}; pub use netupgrade::{Activate, NetUpgrades}; diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index c69128c911..ca888a7fd6 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -485,6 +485,8 @@ impl MempoolBanScore for CheckTransactionError { CheckTransactionError::TxSizeTooLarge(_, _, _) => 100, CheckTransactionError::DeprecatedTokenOperationVersion(_, _) => 100, CheckTransactionError::HtlcsAreNotActivated => 100, + CheckTransactionError::OrdersAreNotActivated(_) => 100, + CheckTransactionError::OrdersCurrenciesMustBeDifferent(_) => 100, } } } From 8c83480f91269aaf303cede8ce9b5e66aca838c2 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 7 May 2024 16:04:11 +0300 Subject: [PATCH 21/37] Rename withdraw and add some docs --- .../scanner-lib/src/blockchain_state/mod.rs | 4 +-- .../scanner-lib/src/sync/tests/simulation.rs | 2 +- api-server/web-server/src/api/json_helpers.rs | 6 ++--- .../src/constraints_accumulator.rs | 2 +- .../src/tests/orders_constraints.rs | 10 +++---- chainstate/src/detail/ban_score.rs | 12 ++++----- chainstate/src/detail/error_classification.rs | 12 ++++----- chainstate/src/rpc/types/account.rs | 4 +-- chainstate/src/rpc/types/output.rs | 2 +- .../src/signature_destination_getter.rs | 4 +-- .../test-suite/src/tests/orders_tests.rs | 20 +++++++------- .../tests/outputs_utils.rs | 2 +- .../src/transaction_verifier/mod.rs | 6 ++--- common/src/chain/order.rs | 16 ++++++++---- common/src/chain/tokens/tokens_utils.rs | 2 +- .../src/chain/transaction/account_outpoint.rs | 4 +-- common/src/chain/transaction/output/mod.rs | 4 +-- mempool/src/error/ban_score.rs | 12 ++++----- mempool/src/pool/entry.rs | 2 +- mintscript/src/translate.rs | 4 +-- orders-accounting/src/cache.rs | 26 +++++++++---------- orders-accounting/src/error.rs | 12 ++++----- orders-accounting/src/operations.rs | 6 ++--- orders-accounting/src/storage/mod.rs | 17 ++++++++++++ orders-accounting/src/tests/operations.rs | 16 ++++++------ wallet/src/account/mod.rs | 6 ++--- wallet/src/account/output_cache/mod.rs | 14 +++++----- 27 files changed, 125 insertions(+), 102 deletions(-) diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index 7fa573789e..e9f1e0c499 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -617,7 +617,7 @@ async fn calculate_fees( | AccountCommand::UnfreezeToken(token_id) | AccountCommand::LockTokenSupply(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) => Some(*token_id), - AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => None, + AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => None, }, }) .collect(); @@ -1106,7 +1106,7 @@ async fn update_tables_from_transaction_inputs( ) .await; } - AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => { + AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { // TODO: support orders } }, diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index e91dbc2f6c..16987bedd3 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -392,7 +392,7 @@ async fn simulation( chain_config.token_change_authority_fee(block_height); burn_coins(&mut statistics, token_change_authority_fee); } - AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::CancelOrder(_) => todo!(), AccountCommand::FillOrder(_, _, _) => todo!(), }, }); diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index de699aeffc..3bf4b7adbe 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -200,7 +200,7 @@ pub fn txoutput_to_json( TxOutput::AnyoneCanTake(data) => { json!({ "type": "AnyoneCanTake", - "withdraw_key": Address::new(chain_config, data.withdraw_key().clone()).expect("no error").as_str(), + "cancel_key": Address::new(chain_config, data.cancel_key().clone()).expect("no error").as_str(), "ask_value": outputvalue_to_json(data.ask(), chain_config, token_decimals), "give_value": outputvalue_to_json(data.give(), chain_config, token_decimals), }) @@ -345,10 +345,10 @@ pub fn tx_input_to_json( "nonce": nonce, }) } - AccountCommand::WithdrawOrder(order_id) => { + AccountCommand::CancelOrder(order_id) => { json!({ "input_type": "AccountCommand", - "command": "WithdrawOrder", + "command": "CancelOrder", "order_id": Address::new(chain_config, *order_id).expect("addressable").to_string(), }) } diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 4bf94b6952..1e4b89a78e 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -266,7 +266,7 @@ impl ConstrainedValueAccumulator { CoinOrTokenId::Coin, chain_config.token_change_authority_fee(block_height), )), - AccountCommand::WithdrawOrder(id) => { + AccountCommand::CancelOrder(id) => { let order_data = orders_accounting_view .get_order_data(id) .map_err(|_| orders_accounting::Error::ViewFail)? diff --git a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs index 2e7108f540..a2b878f744 100644 --- a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs +++ b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs @@ -563,7 +563,7 @@ fn fill_order_constraints(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn withdraw_order_constraints(#[case] seed: Seed) { +fn cancel_order_constraints(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let chain_config = create_unit_test_config(); @@ -593,7 +593,7 @@ fn withdraw_order_constraints(#[case] seed: Seed) { { let inputs = vec![TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), )]; let input_utxos = vec![None]; @@ -628,7 +628,7 @@ fn withdraw_order_constraints(#[case] seed: Seed) { { let inputs = vec![TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), )]; let input_utxos = vec![None]; @@ -665,7 +665,7 @@ fn withdraw_order_constraints(#[case] seed: Seed) { // partially use input in command let inputs = vec![TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), )]; let input_utxos = vec![None]; @@ -700,7 +700,7 @@ fn withdraw_order_constraints(#[case] seed: Seed) { // valid case let inputs = vec![TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), )]; let input_utxos = vec![None]; diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index d78b99d0d7..135b1763a1 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -668,15 +668,15 @@ impl BanScore for orders_accounting::Error { Error::InvariantOrderAskBalanceChangedForUndo(_) => 100, Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => 100, Error::InvariantOrderGiveBalanceChangedForUndo(_) => 100, - Error::InvariantOrderDataExistForWithdrawUndo(_) => 100, - Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => 100, - Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => 100, + Error::InvariantOrderDataExistForCancelUndo(_) => 100, + Error::InvariantOrderAskBalanceExistForCancelUndo(_) => 100, + Error::InvariantOrderGiveBalanceExistForCancelUndo(_) => 100, Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, - Error::AttemptedWithdrawNonexistingOrderData(_) => 100, - Error::AttemptedWithdrawNonexistingAskBalance(_) => 100, - Error::AttemptedWithdrawNonexistingGiveBalance(_) => 100, + Error::AttemptedCancelNonexistingOrderData(_) => 100, + Error::AttemptedCancelNonexistingAskBalance(_) => 100, + Error::AttemptedCancelNonexistingGiveBalance(_) => 100, Error::ViewFail => 0, Error::StorageWrite => 0, } diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index bd0a926a51..8595b90fe1 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -904,15 +904,15 @@ impl BlockProcessingErrorClassification for orders_accounting::Error { | Error::InvariantOrderAskBalanceChangedForUndo(_) | Error::InvariantOrderGiveBalanceNotFoundForUndo(_) | Error::InvariantOrderGiveBalanceChangedForUndo(_) - | Error::InvariantOrderDataExistForWithdrawUndo(_) - | Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) - | Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) + | Error::InvariantOrderDataExistForCancelUndo(_) + | Error::InvariantOrderAskBalanceExistForCancelUndo(_) + | Error::InvariantOrderGiveBalanceExistForCancelUndo(_) | Error::FillOrderChangeLeft(_) | Error::CurrencyMismatch | Error::OrderOverflow(_) - | Error::AttemptedWithdrawNonexistingOrderData(_) - | Error::AttemptedWithdrawNonexistingAskBalance(_) - | Error::AttemptedWithdrawNonexistingGiveBalance(_) => { + | Error::AttemptedCancelNonexistingOrderData(_) + | Error::AttemptedCancelNonexistingAskBalance(_) + | Error::AttemptedCancelNonexistingGiveBalance(_) => { BlockProcessingErrorClass::BadBlock } diff --git a/chainstate/src/rpc/types/account.rs b/chainstate/src/rpc/types/account.rs index 82731fd19d..e11e2d1168 100644 --- a/chainstate/src/rpc/types/account.rs +++ b/chainstate/src/rpc/types/account.rs @@ -74,7 +74,7 @@ pub enum RpcAccountCommand { token_id: RpcAddress, new_authority: RpcAddress, }, - WithdrawOrder { + CancelOrder { order_id: RpcAddress, }, FillOrder { @@ -113,7 +113,7 @@ impl RpcAccountCommand { new_authority: RpcAddress::new(chain_config, destination.clone())?, } } - AccountCommand::WithdrawOrder(id) => RpcAccountCommand::WithdrawOrder { + AccountCommand::CancelOrder(id) => RpcAccountCommand::CancelOrder { order_id: RpcAddress::new(chain_config, *id)?, }, AccountCommand::FillOrder(id, fill, dest) => RpcAccountCommand::FillOrder { diff --git a/chainstate/src/rpc/types/output.rs b/chainstate/src/rpc/types/output.rs index 45b766e087..3622cac5b7 100644 --- a/chainstate/src/rpc/types/output.rs +++ b/chainstate/src/rpc/types/output.rs @@ -209,7 +209,7 @@ impl RpcTxOutput { data: RpcHexString::from_bytes(data), }, TxOutput::AnyoneCanTake(data) => RpcTxOutput::AnyoneCanTake { - authority: RpcAddress::new(chain_config, data.withdraw_key().clone())?, + authority: RpcAddress::new(chain_config, data.cancel_key().clone())?, ask_value: RpcOutputValue::new(chain_config, data.ask().clone())?, give_value: RpcOutputValue::new(chain_config, data.give().clone())?, }, diff --git a/chainstate/test-framework/src/signature_destination_getter.rs b/chainstate/test-framework/src/signature_destination_getter.rs index 1c5041e273..69ea74d150 100644 --- a/chainstate/test-framework/src/signature_destination_getter.rs +++ b/chainstate/test-framework/src/signature_destination_getter.rs @@ -168,7 +168,7 @@ impl<'a> SignatureDestinationGetter<'a> { }; Ok(destination) } - AccountCommand::WithdrawOrder(order_id) => { + AccountCommand::CancelOrder(order_id) => { let order_data = orders_view .get_order_data(order_id) .map_err(|_| { @@ -179,7 +179,7 @@ impl<'a> SignatureDestinationGetter<'a> { .ok_or(SignatureDestinationGetterError::OrderDataNotFound( *order_id, ))?; - Ok(order_data.withdraw_key().clone()) + Ok(order_data.cancel_key().clone()) } AccountCommand::FillOrder(_, _, d) => Ok(d.clone()), }, diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index 676d786b2e..af0c48b7ab 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -299,7 +299,7 @@ fn create_order_tokens_for_tokens(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn withdraw_order_check_storage(#[case] seed: Seed) { +fn cancel_order_check_storage(#[case] seed: Seed) { utils::concurrency::model(move || { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); @@ -325,7 +325,7 @@ fn withdraw_order_check_storage(#[case] seed: Seed) { .add_input( TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -461,7 +461,7 @@ fn fill_order_check_storage(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn withdraw_order_check_signature(#[case] seed: Seed) { +fn cancel_order_check_signature(#[case] seed: Seed) { utils::concurrency::model(move || { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); @@ -485,13 +485,13 @@ fn withdraw_order_check_signature(#[case] seed: Seed) { .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); - // try withdraw without signature + // try cancel without signature { let tx = TransactionBuilder::new() .add_input( TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -515,13 +515,13 @@ fn withdraw_order_check_signature(#[case] seed: Seed) { ) } - // try withdraw with wrong signature + // try cancel with wrong signature { let tx = TransactionBuilder::new() .add_input( TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -573,7 +573,7 @@ fn withdraw_order_check_signature(#[case] seed: Seed) { .add_input( TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -694,7 +694,7 @@ fn reorg_before_create(#[case] seed: Seed) { }); } -// Create a chain with an order which is filled partially and then withdrawn. +// Create a chain with an order which is filled partially and then canceled. // Reorg from a point after the order was created, so that after reorg storage has original information on the order #[rstest] #[trace] @@ -773,7 +773,7 @@ fn reorg_after_create(#[case] seed: Seed) { .add_input( TxInput::AccountCommand( AccountNonce::new(1), - AccountCommand::WithdrawOrder(order_id), + AccountCommand::CancelOrder(order_id), ), InputWitness::NoSignature(None), ) diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs index 31d8fe7fdf..ad284eae0c 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs @@ -154,7 +154,7 @@ pub fn all_account_inputs() -> [TxInput; 9] { ), TxInput::from_command( AccountNonce::new(0), - AccountCommand::WithdrawOrder(OrderId::zero()), + AccountCommand::CancelOrder(OrderId::zero()), ), TxInput::from_command( AccountNonce::new(0), diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 3c4654b2ba..1c534f19e4 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -610,7 +610,7 @@ where }); Some(res) } - AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => None, + AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => None, }, }) .collect::, _>>()?; @@ -751,12 +751,12 @@ where | AccountCommand::FreezeToken(..) | AccountCommand::UnfreezeToken(..) | AccountCommand::ChangeTokenAuthority(..) => None, - AccountCommand::WithdrawOrder(order_id) => { + AccountCommand::CancelOrder(order_id) => { let res = self .spend_input_from_account(*nonce, account_op.clone().into()) .and_then(|_| { self.orders_accounting_cache - .withdraw_order(*order_id) + .cancel_order(*order_id) .map_err(ConnectTransactionError::OrdersAccountingError) }); Some(res) diff --git a/common/src/chain/order.rs b/common/src/chain/order.rs index c9d202e90b..ad7808d95b 100644 --- a/common/src/chain/order.rs +++ b/common/src/chain/order.rs @@ -78,24 +78,30 @@ pub fn make_order_id(input0_outpoint: &UtxoOutPoint) -> OrderId { OrderId::new(hash_encoded(input0_outpoint)) } +/// Order data provides unified data structure to represent an order. +/// There are no buy or sell types of orders per se but rather exchanges. +/// The fields represent currencies and amounts to be exchanged and the trading pair can be deducted from it. #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] pub struct OrderData { - withdraw_key: Destination, + /// The key that can authorize cancellation of an order + cancel_key: Destination, + /// `Ask` and `give` fields represent amounts of currencies + /// that an order maker wants to exchange, e.g. 5 coins for 10 tokens ask: OutputValue, give: OutputValue, } impl OrderData { - pub fn new(withdraw_key: Destination, ask: OutputValue, give: OutputValue) -> Self { + pub fn new(cancel_key: Destination, ask: OutputValue, give: OutputValue) -> Self { Self { - withdraw_key, + cancel_key, ask, give, } } - pub fn withdraw_key(&self) -> &Destination { - &self.withdraw_key + pub fn cancel_key(&self) -> &Destination { + &self.cancel_key } pub fn ask(&self) -> &OutputValue { diff --git a/common/src/chain/tokens/tokens_utils.rs b/common/src/chain/tokens/tokens_utils.rs index 69b963ea53..06427cf4a6 100644 --- a/common/src/chain/tokens/tokens_utils.rs +++ b/common/src/chain/tokens/tokens_utils.rs @@ -56,7 +56,7 @@ pub fn get_token_supply_change_count(inputs: &[TxInput]) -> usize { AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) - | AccountCommand::WithdrawOrder(_) + | AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => false, AccountCommand::MintTokens(_, _) | AccountCommand::UnmintTokens(_) diff --git a/common/src/chain/transaction/account_outpoint.rs b/common/src/chain/transaction/account_outpoint.rs index 8e121cd26d..fc4b3d901e 100644 --- a/common/src/chain/transaction/account_outpoint.rs +++ b/common/src/chain/transaction/account_outpoint.rs @@ -53,7 +53,7 @@ impl From for AccountType { | AccountCommand::FreezeToken(id, _) | AccountCommand::UnfreezeToken(id) | AccountCommand::ChangeTokenAuthority(id, _) => AccountType::Token(id), - AccountCommand::WithdrawOrder(id) | AccountCommand::FillOrder(id, _, _) => { + AccountCommand::CancelOrder(id) | AccountCommand::FillOrder(id, _, _) => { AccountType::Order(id) } } @@ -116,7 +116,7 @@ pub enum AccountCommand { #[codec(index = 5)] ChangeTokenAuthority(TokenId, Destination), #[codec(index = 6)] - WithdrawOrder(OrderId), + CancelOrder(OrderId), #[codec(index = 7)] FillOrder(OrderId, OutputValue, Destination), } diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs index 7761dcde30..68fad37c99 100644 --- a/common/src/chain/transaction/output/mod.rs +++ b/common/src/chain/transaction/output/mod.rs @@ -331,8 +331,8 @@ impl TextSummary for TxOutput { ) } TxOutput::AnyoneCanTake(order) => format!( - "AnyoneCanTake(WithdrawKey({}), AskValue({}), GiveValue({}))", - fmt_dest(order.withdraw_key()), + "AnyoneCanTake(CancelKey({}), AskValue({}), GiveValue({}))", + fmt_dest(order.cancel_key()), fmt_val(order.ask()), fmt_val(order.give()), ), diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index ca888a7fd6..558c7ab808 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -507,15 +507,15 @@ impl MempoolBanScore for orders_accounting::Error { Error::InvariantOrderAskBalanceChangedForUndo(_) => 100, Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => 100, Error::InvariantOrderGiveBalanceChangedForUndo(_) => 100, - Error::InvariantOrderDataExistForWithdrawUndo(_) => 100, - Error::InvariantOrderAskBalanceExistForWithdrawUndo(_) => 100, - Error::InvariantOrderGiveBalanceExistForWithdrawUndo(_) => 100, + Error::InvariantOrderDataExistForCancelUndo(_) => 100, + Error::InvariantOrderAskBalanceExistForCancelUndo(_) => 100, + Error::InvariantOrderGiveBalanceExistForCancelUndo(_) => 100, Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, - Error::AttemptedWithdrawNonexistingOrderData(_) => 0, - Error::AttemptedWithdrawNonexistingAskBalance(_) => 0, - Error::AttemptedWithdrawNonexistingGiveBalance(_) => 0, + Error::AttemptedCancelNonexistingOrderData(_) => 0, + Error::AttemptedCancelNonexistingAskBalance(_) => 0, + Error::AttemptedCancelNonexistingGiveBalance(_) => 0, Error::ViewFail => 0, Error::StorageWrite => 0, } diff --git a/mempool/src/pool/entry.rs b/mempool/src/pool/entry.rs index 932a74801a..ab2055278d 100644 --- a/mempool/src/pool/entry.rs +++ b/mempool/src/pool/entry.rs @@ -75,7 +75,7 @@ impl TxDependency { | AccountCommand::ChangeTokenAuthority(_, _) => { Self::TokenSupplyAccount(TxAccountDependency::new(acct.clone().into(), nonce)) } - AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => { + AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { Self::OrderAccount(TxAccountDependency::new(acct.clone().into(), nonce)) } } diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index 923a4c45fd..c944217380 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -206,7 +206,7 @@ impl TranslateInput for SignedTransaction { }; Ok(checksig(dest)) } - AccountCommand::WithdrawOrder(_) => todo!(), + AccountCommand::CancelOrder(_) => todo!(), AccountCommand::FillOrder(_, _, _) => todo!(), }, } @@ -299,7 +299,7 @@ impl TranslateInput for TimelockOnly { | AccountCommand::FreezeToken(_token_id, _) | AccountCommand::UnfreezeToken(_token_id) | AccountCommand::ChangeTokenAuthority(_token_id, _) => Ok(WitnessScript::TRUE), - AccountCommand::WithdrawOrder(_) | AccountCommand::FillOrder(_, _, _) => { + AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { Ok(WitnessScript::TRUE) } }, diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 079e7868b3..5f21e2321b 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -25,8 +25,8 @@ use crate::{ data::OrdersAccountingDeltaData, error::{Error, Result}, operations::{ - CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, OrdersAccountingUndo, - WithdrawOrderUndo, + CancelOrderUndo, CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, + OrdersAccountingUndo, }, view::OrdersAccountingView, FlushableOrdersAccountingView, OrdersAccountingDeltaUndoData, @@ -83,22 +83,22 @@ impl OrdersAccountingCache

{ Ok(()) } - fn undo_withdraw_order(&mut self, undo: WithdrawOrderUndo) -> Result<()> { + fn undo_withdraw_order(&mut self, undo: CancelOrderUndo) -> Result<()> { ensure!( self.get_order_data(&undo.id)?.is_none(), - Error::InvariantOrderDataExistForWithdrawUndo(undo.id) + Error::InvariantOrderDataExistForCancelUndo(undo.id) ); self.data.order_data.undo_merge_delta_data_element(undo.id, undo.undo_data)?; ensure!( self.get_ask_balance(&undo.id)?.unwrap_or(Amount::ZERO) == Amount::ZERO, - Error::InvariantOrderAskBalanceExistForWithdrawUndo(undo.id) + Error::InvariantOrderAskBalanceExistForCancelUndo(undo.id) ); self.data.ask_balances.add_unsigned(undo.id, undo.ask_balance)?; ensure!( self.get_give_balance(&undo.id)?.unwrap_or(Amount::ZERO) == Amount::ZERO, - Error::InvariantOrderGiveBalanceExistForWithdrawUndo(undo.id) + Error::InvariantOrderGiveBalanceExistForCancelUndo(undo.id) ); self.data.give_balances.add_unsigned(undo.id, undo.give_balance)?; @@ -109,7 +109,7 @@ impl OrdersAccountingCache

{ if let Some(undo_data) = undo.undo_data { ensure!( self.get_order_data(&undo.id)?.is_none(), - Error::InvariantOrderDataExistForWithdrawUndo(undo.id) + Error::InvariantOrderDataExistForCancelUndo(undo.id) ); self.data.order_data.undo_merge_delta_data_element(undo.id, undo_data)?; } @@ -175,16 +175,16 @@ impl OrdersAccountingOperations for OrdersAccountingCac })) } - fn withdraw_order(&mut self, id: OrderId) -> Result { + fn cancel_order(&mut self, id: OrderId) -> Result { let order_data = self .get_order_data(&id)? - .ok_or(Error::AttemptedWithdrawNonexistingOrderData(id))?; + .ok_or(Error::AttemptedCancelNonexistingOrderData(id))?; let ask_balance = self .get_ask_balance(&id)? - .ok_or(Error::AttemptedWithdrawNonexistingAskBalance(id))?; + .ok_or(Error::AttemptedCancelNonexistingAskBalance(id))?; let give_balance = self .get_give_balance(&id)? - .ok_or(Error::AttemptedWithdrawNonexistingGiveBalance(id))?; + .ok_or(Error::AttemptedCancelNonexistingGiveBalance(id))?; let undo_data = self .data @@ -194,7 +194,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac self.data.ask_balances.sub_unsigned(id, ask_balance)?; self.data.give_balances.sub_unsigned(id, give_balance)?; - Ok(OrdersAccountingUndo::WithdrawOrder(WithdrawOrderUndo { + Ok(OrdersAccountingUndo::CancelOrder(CancelOrderUndo { id, undo_data, ask_balance, @@ -240,7 +240,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()> { match undo_data { OrdersAccountingUndo::CreateOrder(undo) => self.undo_create_order(undo), - OrdersAccountingUndo::WithdrawOrder(undo) => self.undo_withdraw_order(undo), + OrdersAccountingUndo::CancelOrder(undo) => self.undo_withdraw_order(undo), OrdersAccountingUndo::FillOrder(undo) => self.undo_fill_order(undo), } } diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index 52771643bf..770adc4b40 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -40,11 +40,11 @@ pub enum Error { #[error("Give balance for order {0}` changed for undo")] InvariantOrderGiveBalanceChangedForUndo(OrderId), #[error("Data for order {0}` still exist on withdraw undo")] - InvariantOrderDataExistForWithdrawUndo(OrderId), + InvariantOrderDataExistForCancelUndo(OrderId), #[error("Ask balance for order {0}` still exist on withdraw undo")] - InvariantOrderAskBalanceExistForWithdrawUndo(OrderId), + InvariantOrderAskBalanceExistForCancelUndo(OrderId), #[error("Give balance for order {0}` still exist on withdraw undo")] - InvariantOrderGiveBalanceExistForWithdrawUndo(OrderId), + InvariantOrderGiveBalanceExistForCancelUndo(OrderId), #[error("Fill operation for order {0}` left a change")] FillOrderChangeLeft(OrderId), #[error("Coin type mismatch")] @@ -52,11 +52,11 @@ pub enum Error { #[error("Order overflow: `{0}`")] OrderOverflow(OrderId), #[error("Attempt to withdraw non-existing order data `{0}`")] - AttemptedWithdrawNonexistingOrderData(OrderId), + AttemptedCancelNonexistingOrderData(OrderId), #[error("Attempt to withdraw non-existing ask balance `{0}`")] - AttemptedWithdrawNonexistingAskBalance(OrderId), + AttemptedCancelNonexistingAskBalance(OrderId), #[error("Attempt to withdraw non-existing give balance `{0}`")] - AttemptedWithdrawNonexistingGiveBalance(OrderId), + AttemptedCancelNonexistingGiveBalance(OrderId), // TODO Need a more granular error reporting in the following // https://github.com/mintlayer/mintlayer-core/issues/811 diff --git a/orders-accounting/src/operations.rs b/orders-accounting/src/operations.rs index 5fc41d124f..0de37bd24d 100644 --- a/orders-accounting/src/operations.rs +++ b/orders-accounting/src/operations.rs @@ -32,7 +32,7 @@ pub struct CreateOrderUndo { } #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] -pub struct WithdrawOrderUndo { +pub struct CancelOrderUndo { pub(crate) id: OrderId, pub(crate) undo_data: DataDeltaUndo, pub(crate) ask_balance: Amount, @@ -51,13 +51,13 @@ pub struct FillOrderUndo { #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, VariantCount)] pub enum OrdersAccountingUndo { CreateOrder(CreateOrderUndo), - WithdrawOrder(WithdrawOrderUndo), + CancelOrder(CancelOrderUndo), FillOrder(FillOrderUndo), } pub trait OrdersAccountingOperations { fn create_order(&mut self, id: OrderId, data: OrderData) -> Result; - fn withdraw_order(&mut self, id: OrderId) -> Result; + fn cancel_order(&mut self, id: OrderId) -> Result; fn fill_order(&mut self, id: OrderId, value: OutputValue) -> Result; fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()>; diff --git a/orders-accounting/src/storage/mod.rs b/orders-accounting/src/storage/mod.rs index deb327af18..0f38a533d6 100644 --- a/orders-accounting/src/storage/mod.rs +++ b/orders-accounting/src/storage/mod.rs @@ -25,8 +25,25 @@ pub mod in_memory; pub trait OrdersAccountingStorageRead { type Error: std::error::Error; + /// Provides access to auxiliary data of an order. fn get_order_data(&self, id: &OrderId) -> Result, Self::Error>; + + /// Provides access to current ask balance. The data represents the remaining amount + /// that is left to satisfy for an order and can be filled by a taker. + /// + /// For example, if an order give 10 coins for 5 tokens this method would return 5. If the order is partially + /// filled and 2 tokens were bought this method would return 3. + /// + /// It's represented by `Amount` to simplify accounting math and the currency can be enquired from OrderData. fn get_ask_balance(&self, id: &OrderId) -> Result, Self::Error>; + + /// Provides access to current give balance. The data represents the remaining amount + /// that can be taken from an order if filled by a taker. + /// + /// For example, if an order gives 10 coins for 5 tokens this method would return 10. If the order is partially + /// filled and 2 tokens were bought this method would return 6. + /// + /// It's represented by `Amount` to simplify accounting math and the currency can be enquired from OrderData. fn get_give_balance(&self, id: &OrderId) -> Result, Self::Error>; } diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index c35c056bd1..3afe2267d7 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -161,14 +161,14 @@ fn withdraw_order_and_flush(#[case] seed: Seed) { // try to withdraw non-existing order { let random_order = OrderId::random_using(&mut rng); - let result = cache.withdraw_order(random_order); + let result = cache.cancel_order(random_order); assert_eq!( result.unwrap_err(), - Error::AttemptedWithdrawNonexistingOrderData(random_order) + Error::AttemptedCancelNonexistingOrderData(random_order) ); } - let _ = cache.withdraw_order(order_id).unwrap(); + let _ = cache.cancel_order(order_id).unwrap(); db.batch_write_orders_data(cache.consume()).unwrap(); @@ -192,11 +192,11 @@ fn withdraw_order_twice(#[case] seed: Seed) { let db = OrdersAccountingDB::new(&storage); let mut cache = OrdersAccountingCache::new(&db); - let _ = cache.withdraw_order(order_id).unwrap(); + let _ = cache.cancel_order(order_id).unwrap(); assert_eq!( - cache.withdraw_order(order_id,), - Err(Error::AttemptedWithdrawNonexistingOrderData(order_id)) + cache.cancel_order(order_id,), + Err(Error::AttemptedCancelNonexistingOrderData(order_id)) ); } @@ -218,7 +218,7 @@ fn withdraw_order_and_undo(#[case] seed: Seed) { let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); - let undo = cache.withdraw_order(order_id).unwrap(); + let undo = cache.cancel_order(order_id).unwrap(); assert_eq!(None, cache.get_order_data(&order_id).unwrap().as_ref()); assert_eq!( @@ -508,7 +508,7 @@ fn fill_order_partially_and_withdraw(#[case] seed: Seed) { cache.get_give_balance(&order_id).unwrap() ); - let _ = cache.withdraw_order(order_id).unwrap(); + let _ = cache.cancel_order(order_id).unwrap(); db.batch_write_orders_data(cache.consume()).unwrap(); diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index af95a6049d..f9254395d1 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1348,7 +1348,7 @@ impl Account { .map(|data| (None, Some(data.authority.clone()))) .ok_or(WalletError::UnknownTokenId(*token_id)), // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), } } @@ -1829,7 +1829,7 @@ impl Account { || self.is_destination_mine_or_watched(address) } // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, }); @@ -2240,7 +2240,7 @@ fn group_preselected_inputs( )?; } // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index bee63017ed..886394e390 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -670,7 +670,7 @@ impl OutputCache { | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) | AccountCommand::UnfreezeToken(_) - | AccountCommand::WithdrawOrder(_) + | AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => None, AccountCommand::FreezeToken(frozen_token_id, _) => Some(frozen_token_id), }, @@ -733,7 +733,7 @@ impl OutputCache { | AccountCommand::ChangeTokenAuthority(token_id, _) | AccountCommand::UnmintTokens(token_id) => frozen_token_id == token_id, // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, TxInput::Account(_) => false, @@ -928,7 +928,7 @@ impl OutputCache { } } // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } @@ -1031,7 +1031,7 @@ impl OutputCache { } } // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } @@ -1324,7 +1324,7 @@ impl OutputCache { } } // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } @@ -1533,7 +1533,7 @@ fn apply_freeze_mutations_from_tx( | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } @@ -1575,7 +1575,7 @@ fn apply_total_supply_mutations_from_tx( | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} // TODO: support orders - AccountCommand::WithdrawOrder(_) => unimplemented!(), + AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } From ec02d7c7d819890ff66ce36264d00da4b80aa7b3 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Thu, 9 May 2024 12:48:46 +0300 Subject: [PATCH 22/37] Avoid panicing on tokens v0 --- .../src/constraints_accumulator.rs | 28 +++++--- .../src/error.rs | 2 + chainstate/src/detail/ban_score.rs | 2 + chainstate/src/detail/error_classification.rs | 8 +-- .../test-suite/src/tests/orders_tests.rs | 67 +++++++++++++++++-- .../chain/transaction/output/output_value.rs | 7 -- common/src/primitives/mod.rs | 8 +-- mempool/src/error/ban_score.rs | 1 + orders-accounting/src/cache.rs | 20 +++--- orders-accounting/src/error.rs | 2 + orders-accounting/src/lib.rs | 9 +++ orders-accounting/src/price_calculation.rs | 14 ++-- orders-accounting/src/tests/operations.rs | 64 +++++++++--------- 13 files changed, 155 insertions(+), 77 deletions(-) diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 1e4b89a78e..6f80cab149 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -280,14 +280,16 @@ impl ConstrainedValueAccumulator { .map_err(|_| orders_accounting::Error::ViewFail)? .ok_or(orders_accounting::Error::OrderGiveBalanceNotFound(*id))?; - let initially_asked = order_data.ask().amount(); + let initially_asked = output_value_amount(order_data.ask())?; let ask_amount = (initially_asked - ask_balance) .ok_or(Error::NegativeAccountBalance(AccountType::Order(*id)))?; - let ask_currency = CoinOrTokenId::from_output_value(order_data.ask()); + let ask_currency = CoinOrTokenId::from_output_value(order_data.ask()) + .ok_or(Error::UnsupportedTokenVersion)?; insert_or_increase(&mut self.unconstrained_value, ask_currency, ask_amount)?; - let give_currency = CoinOrTokenId::from_output_value(order_data.give()); + let give_currency = CoinOrTokenId::from_output_value(order_data.give()) + .ok_or(Error::UnsupportedTokenVersion)?; insert_or_increase(&mut self.unconstrained_value, give_currency, give_balance)?; Ok((CoinOrTokenId::Coin, Amount::ZERO)) } @@ -302,11 +304,13 @@ impl ConstrainedValueAccumulator { .get_order_data(order_id) .map_err(|_| orders_accounting::Error::ViewFail)? .ok_or(orders_accounting::Error::OrderDataNotFound(*order_id))?; - let give_currency = CoinOrTokenId::from_output_value(order_data.give()); + let give_currency = CoinOrTokenId::from_output_value(order_data.give()) + .ok_or(Error::UnsupportedTokenVersion)?; insert_or_increase(&mut self.unconstrained_value, give_currency, filled_amount)?; - let ask_currency = CoinOrTokenId::from_output_value(fill_value); - Ok((ask_currency, fill_value.amount())) + let ask_currency = CoinOrTokenId::from_output_value(fill_value) + .ok_or(Error::UnsupportedTokenVersion)?; + Ok((ask_currency, output_value_amount(fill_value)?)) } } } @@ -375,11 +379,12 @@ impl ConstrainedValueAccumulator { chain_config.nft_issuance_fee(block_height), )?, TxOutput::AnyoneCanTake(order_data) => { - let id = CoinOrTokenId::from_output_value(order_data.give()); + let id = CoinOrTokenId::from_output_value(order_data.give()) + .ok_or(Error::UnsupportedTokenVersion)?; insert_or_increase( &mut accumulator.unconstrained_value, id, - order_data.give().amount(), + output_value_amount(order_data.give())?, )?; } }; @@ -495,3 +500,10 @@ fn decrease_or( } Ok(()) } + +fn output_value_amount(value: &OutputValue) -> Result { + match value { + OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => Ok(*amount), + OutputValue::TokenV0(_) => Err(Error::UnsupportedTokenVersion), + } +} diff --git a/chainstate/constraints-value-accumulator/src/error.rs b/chainstate/constraints-value-accumulator/src/error.rs index e43e94d310..1eba06a970 100644 --- a/chainstate/constraints-value-accumulator/src/error.rs +++ b/chainstate/constraints-value-accumulator/src/error.rs @@ -50,4 +50,6 @@ pub enum Error { AccountBalanceNotFound(AccountType), #[error("Negative account balance for `{0:?}`")] NegativeAccountBalance(AccountType), + #[error("Unsupported token version")] + UnsupportedTokenVersion, } diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 135b1763a1..45edf77efe 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -591,6 +591,7 @@ impl BanScore for constraints_value_accumulator::Error { constraints_value_accumulator::Error::DelegationBalanceNotFound(_) => 0, constraints_value_accumulator::Error::AccountBalanceNotFound(_) => 0, constraints_value_accumulator::Error::NegativeAccountBalance(_) => 100, + constraints_value_accumulator::Error::UnsupportedTokenVersion => 100, constraints_value_accumulator::Error::OrdersAccountingError(err) => err.ban_score(), } } @@ -677,6 +678,7 @@ impl BanScore for orders_accounting::Error { Error::AttemptedCancelNonexistingOrderData(_) => 100, Error::AttemptedCancelNonexistingAskBalance(_) => 100, Error::AttemptedCancelNonexistingGiveBalance(_) => 100, + Error::UnsupportedTokenVersion => 100, Error::ViewFail => 0, Error::StorageWrite => 0, } diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index 8595b90fe1..c5adc832fd 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -881,7 +881,8 @@ impl BlockProcessingErrorClassification for constraints_value_accumulator::Error | Error::MissingOutputOrSpent(_) | Error::PledgeAmountNotFound(_) | Error::SpendingNonSpendableOutput(_) - | Error::NegativeAccountBalance(_) => BlockProcessingErrorClass::BadBlock, + | Error::NegativeAccountBalance(_) + | Error::UnsupportedTokenVersion => BlockProcessingErrorClass::BadBlock, Error::PoSAccountingError(err) => err.classify(), Error::OrdersAccountingError(err) => err.classify(), @@ -912,9 +913,8 @@ impl BlockProcessingErrorClassification for orders_accounting::Error { | Error::OrderOverflow(_) | Error::AttemptedCancelNonexistingOrderData(_) | Error::AttemptedCancelNonexistingAskBalance(_) - | Error::AttemptedCancelNonexistingGiveBalance(_) => { - BlockProcessingErrorClass::BadBlock - } + | Error::AttemptedCancelNonexistingGiveBalance(_) + | Error::UnsupportedTokenVersion => BlockProcessingErrorClass::BadBlock, Error::StorageError(err) => err.classify(), Error::AccountingError(err) => err.classify(), diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index af0c48b7ab..fd71cf1704 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -42,6 +42,13 @@ use tx_verifier::error::{InputCheckError, ScriptError}; use crate::tests::helpers::{issue_token_from_block, mint_tokens_in_block}; +fn output_value_amount(value: &OutputValue) -> Amount { + match value { + OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => *amount, + OutputValue::TokenV0(_) => unreachable!(), + } +} + fn issue_and_mint_token_from_genesis( rng: &mut (impl Rng + CryptoRng), tf: &mut TestFramework, @@ -127,7 +134,7 @@ fn create_order_check_storage(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn create_two_orders_same_tx(#[case] seed: Seed) { +fn create_two_2_orders_same_tx(#[case] seed: Seed) { utils::concurrency::model(move || { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); @@ -146,7 +153,7 @@ fn create_two_orders_same_tx(#[case] seed: Seed) { let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) .add_output(TxOutput::AnyoneCanTake(order_data.clone())) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data)) .build(); let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); @@ -163,6 +170,52 @@ fn create_two_orders_same_tx(#[case] seed: Seed) { }); } +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_two_2_orders_same_block(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let tx1 = TransactionBuilder::new() + .add_input( + tokens_outpoint.clone().into(), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .build(); + let tx2 = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::AnyoneCanTake(order_data)) + .build(); + let block = tf.make_block_builder().with_transactions(vec![tx1, tx2]).build(&mut rng); + let block_id = block.get_id(); + let result = tf.process_block(block, chainstate::BlockSource::Local); + + assert_eq!( + result.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::CheckBlockFailed( + chainstate::CheckBlockError::CheckTransactionFailed( + chainstate::CheckBlockTransactionsError::DuplicateInputInBlock(block_id) + ) + ) + ) + ); + }); +} + #[rstest] #[trace] #[case(Seed::from_entropy())] @@ -383,7 +436,7 @@ fn fill_order_check_storage(#[case] seed: Seed) { let orders_db = OrdersAccountingDB::new(&db_tx); orders_accounting::calculate_fill_order(&orders_db, order_id, &fill_value).unwrap() }; - let left_to_fill = (ask_amount - fill_value.amount()).unwrap(); + let left_to_fill = (ask_amount - output_value_amount(&fill_value)).unwrap(); let tx = TransactionBuilder::new() .add_input(coins_outpoint.into(), InputWitness::NoSignature(None)) @@ -643,7 +696,7 @@ fn reorg_before_create(#[case] seed: Seed) { let orders_db = OrdersAccountingDB::new(&db_tx); orders_accounting::calculate_fill_order(&orders_db, order_id, &fill_value).unwrap() }; - let left_to_fill = (ask_amount - fill_value.amount()).unwrap(); + let left_to_fill = (ask_amount - output_value_amount(&fill_value)).unwrap(); let tx = TransactionBuilder::new() .add_input(coins_outpoint.into(), InputWitness::NoSignature(None)) @@ -737,7 +790,7 @@ fn reorg_after_create(#[case] seed: Seed) { let orders_db = OrdersAccountingDB::new(&db_tx); orders_accounting::calculate_fill_order(&orders_db, order_id, &fill_value).unwrap() }; - let left_to_fill = (ask_amount - fill_value.amount()).unwrap(); + let left_to_fill = (ask_amount - output_value_amount(&fill_value)).unwrap(); tf.make_block_builder() .add_transaction( @@ -801,11 +854,11 @@ fn reorg_after_create(#[case] seed: Seed) { tf.chainstate.get_order_data(&order_id).unwrap() ); assert_eq!( - Some(order_data.ask().amount()), + Some(output_value_amount(order_data.ask())), tf.chainstate.get_order_ask_balance(&order_id).unwrap() ); assert_eq!( - Some(order_data.give().amount()), + Some(output_value_amount(order_data.give())), tf.chainstate.get_order_give_balance(&order_id).unwrap() ); }); diff --git a/common/src/chain/transaction/output/output_value.rs b/common/src/chain/transaction/output/output_value.rs index ea1b51384d..44e739978a 100644 --- a/common/src/chain/transaction/output/output_value.rs +++ b/common/src/chain/transaction/output/output_value.rs @@ -48,13 +48,6 @@ impl OutputValue { OutputValue::TokenV0(_) | OutputValue::TokenV1(_, _) => None, } } - - pub fn amount(&self) -> Amount { - match self { - OutputValue::Coin(v) | OutputValue::TokenV1(_, v) => *v, - OutputValue::TokenV0(_) => panic!("deprecated token version"), - } - } } impl From for OutputValue { diff --git a/common/src/primitives/mod.rs b/common/src/primitives/mod.rs index 9db28b6f17..9226778544 100644 --- a/common/src/primitives/mod.rs +++ b/common/src/primitives/mod.rs @@ -51,11 +51,11 @@ pub enum CoinOrTokenId { } impl CoinOrTokenId { - pub fn from_output_value(value: &OutputValue) -> Self { + pub fn from_output_value(value: &OutputValue) -> Option { match value { - OutputValue::Coin(_) => Self::Coin, - OutputValue::TokenV0(_) => panic!("deprecated token version"), - OutputValue::TokenV1(id, _) => Self::TokenId(*id), + OutputValue::Coin(_) => Some(Self::Coin), + OutputValue::TokenV0(_) => None, + OutputValue::TokenV1(id, _) => Some(Self::TokenId(*id)), } } } diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index 558c7ab808..9a9ced9677 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -516,6 +516,7 @@ impl MempoolBanScore for orders_accounting::Error { Error::AttemptedCancelNonexistingOrderData(_) => 0, Error::AttemptedCancelNonexistingAskBalance(_) => 0, Error::AttemptedCancelNonexistingGiveBalance(_) => 0, + Error::UnsupportedTokenVersion => 100, Error::ViewFail => 0, Error::StorageWrite => 0, } diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 5f21e2321b..750bcf363a 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -28,6 +28,7 @@ use crate::{ CancelOrderUndo, CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, OrdersAccountingUndo, }, + output_value_amount, view::OrdersAccountingView, FlushableOrdersAccountingView, OrdersAccountingDeltaUndoData, }; @@ -157,21 +158,21 @@ impl OrdersAccountingOperations for OrdersAccountingCac return Err(Error::OrderAlreadyExists(id)); } - let ask_value = data.ask().clone(); - let give_value = data.give().clone(); + let ask_amount = output_value_amount(data.ask())?; + let give_amount = output_value_amount(data.give())?; let undo_data = self .data .order_data .merge_delta_data_element(id, accounting::DataDelta::new(None, Some(data)))?; - self.data.ask_balances.add_unsigned(id, ask_value.amount())?; - self.data.give_balances.add_unsigned(id, give_value.amount())?; + self.data.ask_balances.add_unsigned(id, ask_amount)?; + self.data.give_balances.add_unsigned(id, give_amount)?; Ok(OrdersAccountingUndo::CreateOrder(CreateOrderUndo { id, undo_data, - ask_balance: ask_value.amount(), - give_balance: give_value.amount(), + ask_balance: ask_amount, + give_balance: give_amount, })) } @@ -203,6 +204,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac } fn fill_order(&mut self, id: OrderId, fill_value: OutputValue) -> Result { + let fill_amount = output_value_amount(&fill_value)?; let filled_amount = calculate_fill_order(self, id, &fill_value)?; let ask_balance = self.get_ask_balance(&id)?.ok_or(Error::OrderAskBalanceNotFound(id))?; @@ -210,7 +212,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac self.get_give_balance(&id)?.ok_or(Error::OrderGiveBalanceNotFound(id))?; // in case the order is completely filled it can be removed - let undo_data = if fill_value.amount() == ask_balance { + let undo_data = if fill_amount == ask_balance { ensure!( filled_amount == give_balance, Error::FillOrderChangeLeft(id) @@ -227,12 +229,12 @@ impl OrdersAccountingOperations for OrdersAccountingCac }; self.data.give_balances.sub_unsigned(id, filled_amount)?; - self.data.ask_balances.sub_unsigned(id, fill_value.amount())?; + self.data.ask_balances.sub_unsigned(id, fill_amount)?; Ok(OrdersAccountingUndo::FillOrder(FillOrderUndo { id, undo_data, - ask_balance: fill_value.amount(), + ask_balance: fill_amount, give_balance: filled_amount, })) } diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index 770adc4b40..6a59b31033 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -57,6 +57,8 @@ pub enum Error { AttemptedCancelNonexistingAskBalance(OrderId), #[error("Attempt to withdraw non-existing give balance `{0}`")] AttemptedCancelNonexistingGiveBalance(OrderId), + #[error("Unsupported token version")] + UnsupportedTokenVersion, // TODO Need a more granular error reporting in the following // https://github.com/mintlayer/mintlayer-core/issues/811 diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index 7f0849e952..0edae1e4f9 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -34,5 +34,14 @@ pub use { view::{FlushableOrdersAccountingView, OrdersAccountingView}, }; +use common::chain::output_value::OutputValue; + +fn output_value_amount(value: &OutputValue) -> crate::error::Result { + match value { + OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => Ok(*amount), + OutputValue::TokenV0(_) => Err(Error::UnsupportedTokenVersion), + } +} + #[cfg(test)] mod tests; diff --git a/orders-accounting/src/price_calculation.rs b/orders-accounting/src/price_calculation.rs index 3fa1877d30..d7b16b5f2e 100644 --- a/orders-accounting/src/price_calculation.rs +++ b/orders-accounting/src/price_calculation.rs @@ -19,7 +19,7 @@ use common::{ }; use utils::ensure; -use crate::{error::Result, Error, OrdersAccountingView}; +use crate::{error::Result, output_value_amount, Error, OrdersAccountingView}; pub fn calculate_fill_order( view: &impl OrdersAccountingView, @@ -42,14 +42,14 @@ pub fn calculate_fill_order( { let ask_balance = match order_data.ask() { OutputValue::Coin(_) => OutputValue::Coin(ask_balance), - OutputValue::TokenV0(_) => unreachable!(), + OutputValue::TokenV0(_) => return Err(Error::UnsupportedTokenVersion), OutputValue::TokenV1(token_id, _) => OutputValue::TokenV1(*token_id, ask_balance), }; ensure_currencies_and_amounts_match(order_id, &ask_balance, fill_value)?; } - calculate_filled_amount_impl(ask_balance, give_balance, fill_value.amount()) + calculate_filled_amount_impl(ask_balance, give_balance, output_value_amount(fill_value)?) .ok_or(Error::OrderOverflow(order_id)) } @@ -153,8 +153,8 @@ mod tests { order_id, OrderData::new(Destination::AnyoneCanSpend, ask.clone(), give.clone()), )]), - BTreeMap::from_iter([(order_id, ask.amount())]), - BTreeMap::from_iter([(order_id, give.amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(&ask).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(&give).unwrap())]), ); let orders_db = OrdersAccountingDB::new(&orders_store); @@ -184,8 +184,8 @@ mod tests { order_id, OrderData::new(Destination::AnyoneCanSpend, ask.clone(), give.clone()), )]), - BTreeMap::from_iter([(order_id, ask.amount())]), - BTreeMap::from_iter([(order_id, give.amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(&ask).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(&give).unwrap())]), ); let orders_db = OrdersAccountingDB::new(&orders_store); diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index 3afe2267d7..42c553d8ae 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -24,7 +24,7 @@ use rstest::rstest; use test_utils::random::{make_seedable_rng, Seed}; use crate::{ - cache::OrdersAccountingCache, operations::OrdersAccountingOperations, + cache::OrdersAccountingCache, operations::OrdersAccountingOperations, output_value_amount, view::FlushableOrdersAccountingView, Error, InMemoryOrdersAccounting, OrdersAccountingDB, OrdersAccountingView, }; @@ -57,8 +57,8 @@ fn create_order_and_flush(#[case] seed: Seed) { let expected_storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); assert_eq!(expected_storage, storage); @@ -89,8 +89,8 @@ fn create_order_twice(#[case] seed: Seed) { { let storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); let db = OrdersAccountingDB::new(&storage); let mut cache = OrdersAccountingCache::new(&db); @@ -122,11 +122,11 @@ fn create_order_and_undo(#[case] seed: Seed) { cache.get_order_data(&order_id).unwrap().as_ref() ); assert_eq!( - Some(order_data.ask().amount()), + Some(output_value_amount(order_data.ask()).unwrap()), cache.get_ask_balance(&order_id).unwrap() ); assert_eq!( - Some(order_data.give().amount()), + Some(output_value_amount(order_data.give()).unwrap()), cache.get_give_balance(&order_id).unwrap() ); @@ -152,8 +152,8 @@ fn withdraw_order_and_flush(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); @@ -186,8 +186,8 @@ fn withdraw_order_twice(#[case] seed: Seed) { let storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); let db = OrdersAccountingDB::new(&storage); let mut cache = OrdersAccountingCache::new(&db); @@ -211,8 +211,8 @@ fn withdraw_order_and_undo(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); let original_storage = storage.clone(); let mut db = OrdersAccountingDB::new(&mut storage); @@ -237,11 +237,11 @@ fn withdraw_order_and_undo(#[case] seed: Seed) { cache.get_order_data(&order_id).unwrap().as_ref() ); assert_eq!( - Some(order_data.ask().amount()), + Some(output_value_amount(order_data.ask()).unwrap()), cache.get_ask_balance(&order_id).unwrap() ); assert_eq!( - Some(order_data.give().amount()), + Some(output_value_amount(order_data.give()).unwrap()), cache.get_give_balance(&order_id).unwrap() ); @@ -261,8 +261,8 @@ fn fill_entire_order_and_flush(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); @@ -276,7 +276,9 @@ fn fill_entire_order_and_flush(#[case] seed: Seed) { // try to overfill { - let fill = OutputValue::Coin((order_data.ask().amount() + Amount::from_atoms(1)).unwrap()); + let fill = OutputValue::Coin( + (output_value_amount(order_data.ask()).unwrap() + Amount::from_atoms(1)).unwrap(), + ); let result = cache.fill_order(order_id, fill); assert_eq!(result.unwrap_err(), Error::OrderOverflow(order_id)); } @@ -304,8 +306,8 @@ fn fill_order_partially_and_flush(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); @@ -378,8 +380,8 @@ fn fill_order_partially_and_undo(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); let original_storage = storage.clone(); let mut db = OrdersAccountingDB::new(&mut storage); @@ -482,8 +484,8 @@ fn fill_order_partially_and_withdraw(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, order_data.ask().amount())]), - BTreeMap::from_iter([(order_id, order_data.give().amount())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); @@ -523,19 +525,19 @@ fn fill_order_partially_and_withdraw(#[case] seed: Seed) { fn fill_order_must_converge(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); - let ask = rng.gen_range(1u128..1000); - let give = rng.gen_range(1u128..1000); - let fill_orders = test_utils::split_value(&mut rng, ask); + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let fill_orders = test_utils::split_value(&mut rng, ask_amount.into_atoms()); - let ask = OutputValue::Coin(Amount::from_atoms(ask)); - let give = OutputValue::Coin(Amount::from_atoms(give)); + let ask = OutputValue::Coin(ask_amount); + let give = OutputValue::Coin(give_amount); let order_id = OrderId::random_using(&mut rng); let order_data = OrderData::new(Destination::AnyoneCanSpend, ask.clone(), give.clone()); let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, ask.amount())]), - BTreeMap::from_iter([(order_id, give.amount())]), + BTreeMap::from_iter([(order_id, ask_amount)]), + BTreeMap::from_iter([(order_id, give_amount)]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); From 3a5d9605b4a9e3bf2b756d043b74ba9a4f551c66 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Fri, 10 May 2024 15:41:51 +0300 Subject: [PATCH 23/37] Add tests and minor fixes --- .../src/constraints_accumulator.rs | 4 +- .../src/tests/orders_constraints.rs | 6 +- chainstate/src/detail/ban_score.rs | 1 + chainstate/src/detail/error_classification.rs | 1 + .../test-suite/src/tests/orders_tests.rs | 138 +++++++++++++++++- mempool/src/error/ban_score.rs | 1 + orders-accounting/src/cache.rs | 4 +- orders-accounting/src/error.rs | 16 +- orders-accounting/src/price_calculation.rs | 17 ++- orders-accounting/src/tests/operations.rs | 24 +-- 10 files changed, 184 insertions(+), 28 deletions(-) diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 6f80cab149..6f7e429e47 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -281,12 +281,12 @@ impl ConstrainedValueAccumulator { .ok_or(orders_accounting::Error::OrderGiveBalanceNotFound(*id))?; let initially_asked = output_value_amount(order_data.ask())?; - let ask_amount = (initially_asked - ask_balance) + let filled_amount = (initially_asked - ask_balance) .ok_or(Error::NegativeAccountBalance(AccountType::Order(*id)))?; let ask_currency = CoinOrTokenId::from_output_value(order_data.ask()) .ok_or(Error::UnsupportedTokenVersion)?; - insert_or_increase(&mut self.unconstrained_value, ask_currency, ask_amount)?; + insert_or_increase(&mut self.unconstrained_value, ask_currency, filled_amount)?; let give_currency = CoinOrTokenId::from_output_value(order_data.give()) .ok_or(Error::UnsupportedTokenVersion)?; diff --git a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs index a2b878f744..802d7c3c98 100644 --- a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs +++ b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs @@ -307,7 +307,11 @@ fn fill_order_constraints(#[case] seed: Seed) { assert_eq!( result.unwrap_err(), - Error::OrdersAccountingError(orders_accounting::Error::OrderOverflow(order_id)) + Error::OrdersAccountingError(orders_accounting::Error::OrderOverbid( + order_id, + ask_amount, + (ask_amount + Amount::from_atoms(1)).unwrap() + )) ); } diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 45edf77efe..0ab9254537 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -675,6 +675,7 @@ impl BanScore for orders_accounting::Error { Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, + Error::OrderOverbid(_, _, _) => 100, Error::AttemptedCancelNonexistingOrderData(_) => 100, Error::AttemptedCancelNonexistingAskBalance(_) => 100, Error::AttemptedCancelNonexistingGiveBalance(_) => 100, diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index c5adc832fd..15a3b339a0 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -911,6 +911,7 @@ impl BlockProcessingErrorClassification for orders_accounting::Error { | Error::FillOrderChangeLeft(_) | Error::CurrencyMismatch | Error::OrderOverflow(_) + | Error::OrderOverbid(_, _, _) | Error::AttemptedCancelNonexistingOrderData(_) | Error::AttemptedCancelNonexistingAskBalance(_) | Error::AttemptedCancelNonexistingGiveBalance(_) diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index fd71cf1704..8ae99c1a4c 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -17,6 +17,7 @@ use chainstate::ConnectTransactionError; use chainstate_storage::Transactional; use chainstate_test_framework::{TestFramework, TransactionBuilder}; use common::{ + address::pubkeyhash::PublicKeyHash, chain::{ make_order_id, output_value::OutputValue, @@ -134,7 +135,7 @@ fn create_order_check_storage(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn create_two_2_orders_same_tx(#[case] seed: Seed) { +fn create_two_same_orders_in_tx(#[case] seed: Seed) { utils::concurrency::model(move || { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); @@ -173,7 +174,52 @@ fn create_two_2_orders_same_tx(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn create_two_2_orders_same_block(#[case] seed: Seed) { +fn create_two_orders_same_tx(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); + + let amount1 = Amount::from_atoms(rng.gen_range(1u128..1000)); + let amount2 = Amount::from_atoms(rng.gen_range(1u128..1000)); + let order_data_1 = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(amount1), + OutputValue::TokenV1(token_id, amount2), + ); + + let order_data_2 = OrderData::new( + Destination::PublicKeyHash(PublicKeyHash::random()), + OutputValue::Coin(amount2), + OutputValue::TokenV1(token_id, amount1), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::AnyoneCanTake(order_data_1)) + .add_output(TxOutput::AnyoneCanTake(order_data_2)) + .build(); + let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + + assert_eq!( + result.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::StateUpdateFailed( + chainstate::ConnectTransactionError::OrdersAccountingError( + orders_accounting::Error::OrderAlreadyExists(order_id) + ) + ) + ) + ); + }); +} + +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn create_two_orders_same_block(#[case] seed: Seed) { utils::concurrency::model(move || { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); @@ -511,6 +557,94 @@ fn fill_order_check_storage(#[case] seed: Seed) { }); } +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_partially_then_cancel(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, coins_outpoint) = + issue_and_mint_token_from_genesis(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + // Fill the order partially + let fill_value = OutputValue::Coin(Amount::from_atoms( + rng.gen_range(1..ask_amount.into_atoms()), + )); + let filled_amount = { + let db_tx = tf.storage.transaction_ro().unwrap(); + let orders_db = OrdersAccountingDB::new(&db_tx); + orders_accounting::calculate_fill_order(&orders_db, order_id, &fill_value).unwrap() + }; + + let tx = TransactionBuilder::new() + .add_input(coins_outpoint.into(), InputWitness::NoSignature(None)) + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder( + order_id, + fill_value.clone(), + Destination::AnyoneCanSpend, + ), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, filled_amount), + Destination::AnyoneCanSpend, + )) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + // cancel the order + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(1), + AccountCommand::CancelOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, (give_amount - filled_amount).unwrap()), + Destination::AnyoneCanSpend, + )) + .add_output(TxOutput::Transfer( + OutputValue::Coin(output_value_amount(&fill_value)), + Destination::AnyoneCanSpend, + )) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap()); + assert_eq!( + None, + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + None, + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + }); +} + #[rstest] #[trace] #[case(Seed::from_entropy())] diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index 9a9ced9677..51a665865e 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -513,6 +513,7 @@ impl MempoolBanScore for orders_accounting::Error { Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, + Error::OrderOverbid(_, _, _) => 100, Error::AttemptedCancelNonexistingOrderData(_) => 0, Error::AttemptedCancelNonexistingAskBalance(_) => 0, Error::AttemptedCancelNonexistingGiveBalance(_) => 0, diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 750bcf363a..0b6a903295 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -84,7 +84,7 @@ impl OrdersAccountingCache

{ Ok(()) } - fn undo_withdraw_order(&mut self, undo: CancelOrderUndo) -> Result<()> { + fn undo_cancel_order(&mut self, undo: CancelOrderUndo) -> Result<()> { ensure!( self.get_order_data(&undo.id)?.is_none(), Error::InvariantOrderDataExistForCancelUndo(undo.id) @@ -242,7 +242,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()> { match undo_data { OrdersAccountingUndo::CreateOrder(undo) => self.undo_create_order(undo), - OrdersAccountingUndo::CancelOrder(undo) => self.undo_withdraw_order(undo), + OrdersAccountingUndo::CancelOrder(undo) => self.undo_cancel_order(undo), OrdersAccountingUndo::FillOrder(undo) => self.undo_fill_order(undo), } } diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index 6a59b31033..14753ac94b 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use common::chain::OrderId; +use common::{chain::OrderId, primitives::Amount}; #[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] pub enum Error { @@ -39,11 +39,11 @@ pub enum Error { InvariantOrderGiveBalanceNotFoundForUndo(OrderId), #[error("Give balance for order {0}` changed for undo")] InvariantOrderGiveBalanceChangedForUndo(OrderId), - #[error("Data for order {0}` still exist on withdraw undo")] + #[error("Data for order {0}` still exist on cancel undo")] InvariantOrderDataExistForCancelUndo(OrderId), - #[error("Ask balance for order {0}` still exist on withdraw undo")] + #[error("Ask balance for order {0}` still exist on cancel undo")] InvariantOrderAskBalanceExistForCancelUndo(OrderId), - #[error("Give balance for order {0}` still exist on withdraw undo")] + #[error("Give balance for order {0}` still exist on cancel undo")] InvariantOrderGiveBalanceExistForCancelUndo(OrderId), #[error("Fill operation for order {0}` left a change")] FillOrderChangeLeft(OrderId), @@ -51,11 +51,13 @@ pub enum Error { CurrencyMismatch, #[error("Order overflow: `{0}`")] OrderOverflow(OrderId), - #[error("Attempt to withdraw non-existing order data `{0}`")] + #[error("Order `{0}` can provide `{1:?}` amount; but attempted to fill `{2:?}`")] + OrderOverbid(OrderId, Amount, Amount), + #[error("Attempt to cancel non-existing order data `{0}`")] AttemptedCancelNonexistingOrderData(OrderId), - #[error("Attempt to withdraw non-existing ask balance `{0}`")] + #[error("Attempt to cancel non-existing ask balance `{0}`")] AttemptedCancelNonexistingAskBalance(OrderId), - #[error("Attempt to withdraw non-existing give balance `{0}`")] + #[error("Attempt to cancel non-existing give balance `{0}`")] AttemptedCancelNonexistingGiveBalance(OrderId), #[error("Unsupported token version")] UnsupportedTokenVersion, diff --git a/orders-accounting/src/price_calculation.rs b/orders-accounting/src/price_calculation.rs index d7b16b5f2e..ad005a8a24 100644 --- a/orders-accounting/src/price_calculation.rs +++ b/orders-accounting/src/price_calculation.rs @@ -64,11 +64,17 @@ fn ensure_currencies_and_amounts_match( ) -> Result<()> { match (left, right) { (OutputValue::Coin(amount1), OutputValue::Coin(amount2)) => { - ensure!(amount1 >= amount2, Error::OrderOverflow(order_id)); + ensure!( + amount1 >= amount2, + Error::OrderOverbid(order_id, *amount1, *amount2) + ); Ok(()) } (OutputValue::TokenV1(id1, amount1), OutputValue::TokenV1(id2, amount2)) => { - ensure!(amount1 >= amount2, Error::OrderOverflow(order_id)); + ensure!( + amount1 >= amount2, + Error::OrderOverbid(order_id, *amount1, *amount2) + ); ensure!(id1 == id2, Error::CurrencyMismatch); Ok(()) } @@ -166,9 +172,10 @@ mod tests { #[rstest] #[case(token!(0), coin!(1), token!(0), Error::OrderOverflow(OrderId::zero()))] - #[case(token!(0), coin!(1), token!(1), Error::OrderOverflow(OrderId::zero()))] - #[case(coin!(1), token!(1), coin!(2), Error::OrderOverflow(OrderId::zero()))] - #[case(coin!(1), token!(u128::MAX), coin!(2), Error::OrderOverflow(OrderId::zero()))] + #[case(token!(0), coin!(1), token!(1), Error::OrderOverbid(OrderId::zero(), Amount::from_atoms(0), Amount::from_atoms(1)))] + #[case(coin!(1), token!(1), coin!(2), Error::OrderOverbid(OrderId::zero(), Amount::from_atoms(1), Amount::from_atoms(2)))] + #[case(coin!(1), token!(u128::MAX), coin!(2), Error::OrderOverbid(OrderId::zero(), Amount::from_atoms(1), Amount::from_atoms(2)))] + #[case(coin!(2), token!(u128::MAX), coin!(2), Error::OrderOverflow(OrderId::zero()))] #[case(coin!(1), token!(1), token!(1), Error::CurrencyMismatch)] #[case(coin!(1), token!(1), token!(1), Error::CurrencyMismatch)] #[case(token!(1), token2!(1), token2!(1), Error::CurrencyMismatch)] diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index 42c553d8ae..3aaa79d52f 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -144,7 +144,7 @@ fn create_order_and_undo(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn withdraw_order_and_flush(#[case] seed: Seed) { +fn cancel_order_and_flush(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let order_id = OrderId::random_using(&mut rng); @@ -158,7 +158,7 @@ fn withdraw_order_and_flush(#[case] seed: Seed) { let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); - // try to withdraw non-existing order + // try to cancel non-existing order { let random_order = OrderId::random_using(&mut rng); let result = cache.cancel_order(random_order); @@ -178,7 +178,7 @@ fn withdraw_order_and_flush(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn withdraw_order_twice(#[case] seed: Seed) { +fn cancel_order_twice(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let order_id = OrderId::random_using(&mut rng); @@ -203,7 +203,7 @@ fn withdraw_order_twice(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn withdraw_order_and_undo(#[case] seed: Seed) { +fn cancel_order_and_undo(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let order_id = OrderId::random_using(&mut rng); @@ -276,11 +276,17 @@ fn fill_entire_order_and_flush(#[case] seed: Seed) { // try to overfill { - let fill = OutputValue::Coin( - (output_value_amount(order_data.ask()).unwrap() + Amount::from_atoms(1)).unwrap(), - ); + let ask_amount = output_value_amount(order_data.ask()).unwrap(); + let fill = OutputValue::Coin((ask_amount + Amount::from_atoms(1)).unwrap()); let result = cache.fill_order(order_id, fill); - assert_eq!(result.unwrap_err(), Error::OrderOverflow(order_id)); + assert_eq!( + result.unwrap_err(), + Error::OrderOverbid( + order_id, + ask_amount, + (ask_amount + Amount::from_atoms(1)).unwrap() + ) + ); } let _ = cache.fill_order(order_id, order_data.ask().clone()).unwrap(); @@ -471,7 +477,7 @@ fn fill_order_partially_and_undo(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn fill_order_partially_and_withdraw(#[case] seed: Seed) { +fn fill_order_partially_and_cancel(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let order_id = OrderId::random_using(&mut rng); From fabea9728ee8da6967333665c29d832eccf2df85 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Fri, 17 May 2024 15:54:22 +0300 Subject: [PATCH 24/37] Support orders in RandomTxMaker --- chainstate/src/detail/ban_score.rs | 1 + chainstate/src/detail/error_classification.rs | 1 + chainstate/storage/src/internal/expensive.rs | 29 ++ .../test-framework/src/block_builder.rs | 24 +- chainstate/test-framework/src/lib.rs | 2 +- .../test-framework/src/pos_block_builder.rs | 24 +- .../test-framework/src/random_tx_maker.rs | 388 ++++++++++++++++-- chainstate/test-framework/src/utils.rs | 15 + .../test-suite/src/tests/orders_tests.rs | 9 +- common/src/chain/tokens/issuance.rs | 10 +- mempool/src/error/ban_score.rs | 1 + orders-accounting/src/cache.rs | 20 +- orders-accounting/src/error.rs | 2 + orders-accounting/src/tests/operations.rs | 2 +- test-utils/src/nft_utils.rs | 18 +- 15 files changed, 474 insertions(+), 72 deletions(-) diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 0ab9254537..d2c30b83cf 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -664,6 +664,7 @@ impl BanScore for orders_accounting::Error { Error::OrderDataNotFound(_) => 100, Error::OrderAskBalanceNotFound(_) => 100, Error::OrderGiveBalanceNotFound(_) => 100, + Error::OrderWithZeroValue(_) => 100, Error::InvariantOrderDataNotFoundForUndo(_) => 100, Error::InvariantOrderAskBalanceNotFoundForUndo(_) => 100, Error::InvariantOrderAskBalanceChangedForUndo(_) => 100, diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index 15a3b339a0..c2e84a1dfc 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -900,6 +900,7 @@ impl BlockProcessingErrorClassification for orders_accounting::Error { | Error::OrderDataNotFound(_) | Error::OrderAskBalanceNotFound(_) | Error::OrderGiveBalanceNotFound(_) + | Error::OrderWithZeroValue(_) | Error::InvariantOrderDataNotFoundForUndo(_) | Error::InvariantOrderAskBalanceNotFoundForUndo(_) | Error::InvariantOrderAskBalanceChangedForUndo(_) diff --git a/chainstate/storage/src/internal/expensive.rs b/chainstate/storage/src/internal/expensive.rs index 012cbc811c..ab1ada3aa6 100644 --- a/chainstate/storage/src/internal/expensive.rs +++ b/chainstate/storage/src/internal/expensive.rs @@ -145,4 +145,33 @@ impl StoreTxRo<'_, B> { circulating_supply, }) } + + #[log_error] + pub fn read_orders_accounting_data( + &self, + ) -> crate::Result { + let order_data = self + .0 + .get::() + .prefix_iter_decoded(&())? + .collect::>(); + + let ask_balances = self + .0 + .get::() + .prefix_iter_decoded(&())? + .collect::>(); + + let give_balances = self + .0 + .get::() + .prefix_iter_decoded(&())? + .collect::>(); + + Ok(orders_accounting::OrdersAccountingData { + order_data, + ask_balances, + give_balances, + }) + } } diff --git a/chainstate/test-framework/src/block_builder.rs b/chainstate/test-framework/src/block_builder.rs index a9c41c5245..08e2cf8d56 100644 --- a/chainstate/test-framework/src/block_builder.rs +++ b/chainstate/test-framework/src/block_builder.rs @@ -59,6 +59,7 @@ pub struct BlockBuilder<'f> { account_nonce_tracker: BTreeMap, tokens_accounting_store: InMemoryTokensAccounting, pos_accounting_store: InMemoryPoSAccounting, + orders_accounting_store: InMemoryOrdersAccounting, } impl<'f> BlockBuilder<'f> { @@ -92,6 +93,18 @@ impl<'f> BlockBuilder<'f> { .unwrap(); let pos_accounting_store = InMemoryPoSAccounting::from_data(all_pos_accounting_data); + let all_orders_data = framework + .storage + .transaction_ro() + .unwrap() + .read_orders_accounting_data() + .unwrap(); + let orders_accounting_store = InMemoryOrdersAccounting::from_values( + all_orders_data.order_data, + all_orders_data.ask_balances, + all_orders_data.give_balances, + ); + Self { framework, transactions, @@ -105,6 +118,7 @@ impl<'f> BlockBuilder<'f> { account_nonce_tracker, tokens_accounting_store, pos_accounting_store, + orders_accounting_store, } } @@ -145,12 +159,13 @@ impl<'f> BlockBuilder<'f> { }) }); - let (tx, new_tokens_delta, new_pos_accounting_delta) = + let (tx, new_tokens_delta, new_pos_accounting_delta, new_orders_accounting_delta) = super::random_tx_maker::RandomTxMaker::new( &self.framework.chainstate, &utxo_set, &self.tokens_accounting_store, &self.pos_accounting_store, + &self.orders_accounting_store, None, account_nonce_getter, support_htlc, @@ -166,8 +181,7 @@ impl<'f> BlockBuilder<'f> { // spending destinations could change let tokens_db = TokensAccountingDB::new(&self.tokens_accounting_store); let pos_db = PoSAccountingDB::new(&self.pos_accounting_store); - let orders_store = InMemoryOrdersAccounting::new(); - let orders_db = OrdersAccountingDB::new(&orders_store); + let orders_db = OrdersAccountingDB::new(&self.orders_accounting_store); let destination_getter = SignatureDestinationGetter::new_for_transaction( &tokens_db, &pos_db, &orders_db, &utxo_set, ); @@ -185,6 +199,10 @@ impl<'f> BlockBuilder<'f> { let mut tokens_db = TokensAccountingDB::new(&mut self.tokens_accounting_store); tokens_db.merge_with_delta(new_tokens_delta).unwrap(); + // flush new orders info to the in-memory store + let mut orders_db = OrdersAccountingDB::new(&mut self.orders_accounting_store); + orders_db.merge_with_delta(new_orders_accounting_delta).unwrap(); + // flush new pos accounting info to the in-memory store let mut pos_db = PoSAccountingDB::new(&mut self.pos_accounting_store); pos_db.merge_with_delta(new_pos_accounting_delta).unwrap(); diff --git a/chainstate/test-framework/src/lib.rs b/chainstate/test-framework/src/lib.rs index d1e69b4a39..b0631153f0 100644 --- a/chainstate/test-framework/src/lib.rs +++ b/chainstate/test-framework/src/lib.rs @@ -40,7 +40,7 @@ pub use { anyonecanspend_address, create_chain_config_with_default_staking_pool, create_chain_config_with_staking_pool, create_custom_genesis_with_stake_pool, create_stake_pool_data_with_all_reward_to_staker, empty_witness, get_output_value, - pos_mine, produce_kernel_signature, + output_value_amount, pos_mine, produce_kernel_signature, }, block_builder::BlockBuilder, framework::TestFramework, diff --git a/chainstate/test-framework/src/pos_block_builder.rs b/chainstate/test-framework/src/pos_block_builder.rs index eca35a965f..d45d1b8abb 100644 --- a/chainstate/test-framework/src/pos_block_builder.rs +++ b/chainstate/test-framework/src/pos_block_builder.rs @@ -70,6 +70,7 @@ pub struct PoSBlockBuilder<'f> { account_nonce_tracker: BTreeMap, tokens_accounting_store: InMemoryTokensAccounting, pos_accounting_store: InMemoryPoSAccounting, + orders_accounting_store: InMemoryOrdersAccounting, } impl<'f> PoSBlockBuilder<'f> { @@ -98,6 +99,18 @@ impl<'f> PoSBlockBuilder<'f> { .unwrap(); let pos_accounting_store = InMemoryPoSAccounting::from_data(all_pos_accounting_data); + let all_orders_data = framework + .storage + .transaction_ro() + .unwrap() + .read_orders_accounting_data() + .unwrap(); + let orders_accounting_store = InMemoryOrdersAccounting::from_values( + all_orders_data.order_data, + all_orders_data.ask_balances, + all_orders_data.give_balances, + ); + Self { framework, transactions, @@ -113,6 +126,7 @@ impl<'f> PoSBlockBuilder<'f> { account_nonce_tracker: BTreeMap::new(), tokens_accounting_store, pos_accounting_store, + orders_accounting_store, } } @@ -385,12 +399,13 @@ impl<'f> PoSBlockBuilder<'f> { }) }); - let (tx, new_tokens_delta, new_pos_accounting_delta) = + let (tx, new_tokens_delta, new_pos_accounting_delta, new_orders_accounting_delta) = super::random_tx_maker::RandomTxMaker::new( &self.framework.chainstate, &utxo_set, &self.tokens_accounting_store, &self.pos_accounting_store, + &self.orders_accounting_store, self.staking_pool, account_nonce_getter, support_htlc, @@ -406,8 +421,7 @@ impl<'f> PoSBlockBuilder<'f> { // spending destinations could change let tokens_db = TokensAccountingDB::new(&self.tokens_accounting_store); let pos_db = PoSAccountingDB::new(&self.pos_accounting_store); - let orders_store = InMemoryOrdersAccounting::new(); - let orders_db = OrdersAccountingDB::new(&orders_store); + let orders_db = OrdersAccountingDB::new(&self.orders_accounting_store); let destination_getter = SignatureDestinationGetter::new_for_transaction( &tokens_db, &pos_db, &orders_db, &utxo_set, ); @@ -425,6 +439,10 @@ impl<'f> PoSBlockBuilder<'f> { let mut tokens_db = TokensAccountingDB::new(&mut self.tokens_accounting_store); tokens_db.merge_with_delta(new_tokens_delta).unwrap(); + // flush new orders info to the in-memory store + let mut orders_db = OrdersAccountingDB::new(&mut self.orders_accounting_store); + orders_db.merge_with_delta(new_orders_accounting_delta).unwrap(); + // flush new pos accounting info to the in-memory store let mut pos_db = PoSAccountingDB::new(&mut self.pos_accounting_store); pos_db.merge_with_delta(new_pos_accounting_delta).unwrap(); diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index 2efb62b182..804c009ea8 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -15,12 +15,17 @@ use std::collections::BTreeMap; -use crate::{key_manager::KeyManager, TestChainstate}; +use crate::{ + key_manager::KeyManager, + utils::{output_value_amount, output_value_with_amount}, + TestChainstate, +}; use chainstate::chainstate_interface::ChainstateInterface; use common::{ chain::{ htlc::{HashedTimelockContract, HtlcSecretHash}, + make_order_id, output_value::OutputValue, stakelock::StakePoolData, timelock::OutputTimeLock, @@ -29,15 +34,19 @@ use common::{ TokenTotalSupply, }, AccountCommand, AccountNonce, AccountOutPoint, AccountSpending, AccountType, DelegationId, - Destination, GenBlockId, OutPointSourceId, PoolId, Transaction, TxInput, TxOutput, - UtxoOutPoint, + Destination, GenBlockId, OrderData, OrderId, OutPointSourceId, PoolId, Transaction, + TxInput, TxOutput, UtxoOutPoint, }, - primitives::{per_thousand::PerThousand, Amount, BlockHeight, Id, Idable, H256}, + primitives::{per_thousand::PerThousand, Amount, BlockHeight, CoinOrTokenId, Id, Idable, H256}, }; use crypto::{ key::{KeyKind, PrivateKey}, vrf::{VRFKeyKind, VRFPrivateKey}, }; +use orders_accounting::{ + InMemoryOrdersAccounting, OrdersAccountingCache, OrdersAccountingDB, OrdersAccountingDeltaData, + OrdersAccountingOperations, OrdersAccountingView, +}; use pos_accounting::{ make_pool_id, DelegationData, InMemoryPoSAccounting, PoSAccountingDB, PoSAccountingDelta, PoSAccountingDeltaData, PoSAccountingOperations, PoSAccountingUndo, PoSAccountingView, @@ -59,9 +68,10 @@ fn get_random_pool_data<'a>( storage: &'a InMemoryPoSAccounting, tip_view: &impl PoSAccountingView, ) -> Option<(&'a PoolId, &'a PoolData)> { - let all_pool_data = storage.all_pool_data(); - (!all_pool_data.is_empty()) - .then(|| all_pool_data.iter().choose(rng).unwrap()) + storage + .all_pool_data() + .iter() + .choose(rng) .and_then(|(id, data)| tip_view.pool_exists(*id).unwrap().then_some((id, data))) } @@ -70,11 +80,48 @@ fn get_random_delegation_data<'a>( storage: &'a InMemoryPoSAccounting, tip_view: &impl PoSAccountingView, ) -> Option<(&'a DelegationId, &'a DelegationData)> { - let all_delegation_data = storage.all_delegation_data(); - (!all_delegation_data.is_empty()) - .then(|| all_delegation_data.iter().choose(rng).unwrap()) - .and_then(|(id, data)| { - tip_view.get_delegation_data(*id).unwrap().is_some().then_some((id, data)) + storage.all_delegation_data().iter().choose(rng).and_then(|(id, data)| { + tip_view.get_delegation_data(*id).unwrap().is_some().then_some((id, data)) + }) +} + +fn get_random_token<'a>( + rng: &mut impl Rng, + storage: &'a InMemoryTokensAccounting, + tip_view: &'a impl TokensAccountingView, +) -> Option<(TokenId, Amount)> { + storage + .tokens_data() + .iter() + .choose(rng) + .and_then(|(token_id, _)| { + tip_view.get_token_data(token_id).unwrap().is_some().then_some(*token_id) + }) + .and_then(|token_id| { + tip_view + .get_circulating_supply(&token_id) + .unwrap() + .map(|supply| (token_id, supply)) + }) +} + +fn get_random_order_to_fill<'a>( + storage: &'a InMemoryOrdersAccounting, + tip_view: &'a impl OrdersAccountingView, + value: &OutputValue, +) -> Option { + storage + .orders_data() + .iter() + .filter(|(id, _)| tip_view.get_order_data(id).unwrap().is_some()) + .find_map(|(order_id, data)| { + let same_currency = CoinOrTokenId::from_output_value(data.ask()) + == CoinOrTokenId::from_output_value(value); + let order_not_overbid = tip_view + .get_ask_balance(order_id) + .unwrap() + .is_some_and(|ask| ask >= output_value_amount(value)); + (same_currency && order_not_overbid).then_some(*order_id) }) } @@ -129,6 +176,7 @@ pub struct RandomTxMaker<'a> { utxo_set: &'a UtxosDBInMemoryImpl, tokens_store: &'a InMemoryTokensAccounting, pos_accounting_store: &'a InMemoryPoSAccounting, + orders_store: &'a InMemoryOrdersAccounting, // Pool used for staking cannot be spent staking_pool: Option, @@ -139,6 +187,7 @@ pub struct RandomTxMaker<'a> { // Transaction is composed of multiple inputs and outputs // but pools, delegations and tokens can be created only once per transaction token_can_be_issued: bool, + order_can_be_created: bool, stake_pool_can_be_created: bool, delegation_can_be_created: bool, @@ -161,6 +210,7 @@ impl<'a> RandomTxMaker<'a> { utxo_set: &'a UtxosDBInMemoryImpl, tokens_store: &'a InMemoryTokensAccounting, pos_accounting_store: &'a InMemoryPoSAccounting, + orders_store: &'a InMemoryOrdersAccounting, staking_pool: Option, account_nonce_getter: Box Option + 'a>, support_htlc: bool, @@ -170,10 +220,12 @@ impl<'a> RandomTxMaker<'a> { utxo_set, tokens_store, pos_accounting_store, + orders_store, staking_pool, account_nonce_getter, account_nonce_tracker: BTreeMap::new(), token_can_be_issued: true, + order_can_be_created: true, stake_pool_can_be_created: true, delegation_can_be_created: true, account_command_used: false, @@ -193,6 +245,7 @@ impl<'a> RandomTxMaker<'a> { Transaction, TokensAccountingDeltaData, PoSAccountingDeltaData, + OrdersAccountingDeltaData, ) { let tokens_db = TokensAccountingDB::new(self.tokens_store); let mut tokens_cache = TokensAccountingCache::new(&tokens_db); @@ -200,6 +253,9 @@ impl<'a> RandomTxMaker<'a> { let pos_db = PoSAccountingDB::new(self.pos_accounting_store); let mut pos_delta = PoSAccountingDelta::new(&pos_db); + let orders_db = OrdersAccountingDB::new(self.orders_store); + let mut orders_cache = OrdersAccountingCache::new(&orders_db); + // Select random number of utxos to spend let inputs_with_utxos = { let mut inputs_with_utxos = self.select_utxos(rng); @@ -236,6 +292,7 @@ impl<'a> RandomTxMaker<'a> { staking_pools_observer, &mut tokens_cache, &mut pos_delta, + &mut orders_cache, key_manager, inputs_with_utxos, ); @@ -248,6 +305,7 @@ impl<'a> RandomTxMaker<'a> { &mut tokens_cache, &pos_db, &mut pos_delta, + &mut orders_cache, &account_inputs, key_manager, ); @@ -263,8 +321,14 @@ impl<'a> RandomTxMaker<'a> { outputs.shuffle(rng); // now that the inputs are in place calculate the ids and replace dummy values - let (outputs, new_staking_pools) = - Self::tx_outputs_post_process(rng, &mut pos_delta, &inputs, outputs); + let (outputs, new_staking_pools) = Self::tx_outputs_post_process( + rng, + &mut pos_delta, + &mut tokens_cache, + &mut orders_cache, + &inputs, + outputs, + ); let tx = Transaction::new(0, inputs, outputs).unwrap(); let tx_id = tx.get_id(); @@ -293,7 +357,12 @@ impl<'a> RandomTxMaker<'a> { } }); - (tx, tokens_cache.consume(), pos_delta.consume()) + ( + tx, + tokens_cache.consume(), + pos_delta.consume(), + orders_cache.consume(), + ) } fn select_utxos(&self, rng: &mut impl Rng) -> Vec<(TxInput, TxOutput)> { @@ -303,7 +372,29 @@ impl<'a> RandomTxMaker<'a> { .iter() .choose_multiple(rng, number_of_inputs) .iter() - .map(|(outpoint, utxo)| (TxInput::Utxo((*outpoint).clone()), utxo.output().clone())) + .filter_map(|(outpoint, utxo)| { + let input = TxInput::Utxo((*outpoint).clone()); + let input_utxo = utxo.output().clone(); + match input_utxo { + TxOutput::LockThenTransfer(_, _, timelock) => { + self.check_timelock(&input, &timelock) + } + TxOutput::Htlc(_, ref htlc) => { + self.check_timelock(&input, &htlc.refund_timelock) + } + TxOutput::Transfer(_, _) + | TxOutput::Burn(_) + | TxOutput::CreateStakePool(_, _) + | TxOutput::ProduceBlockFromStake(_, _) + | TxOutput::CreateDelegationId(_, _) + | TxOutput::DelegateStaking(_, _) + | TxOutput::IssueFungibleToken(_) + | TxOutput::IssueNft(_, _, _) + | TxOutput::DataDeposit(_) + | TxOutput::AnyoneCanTake(_) => true, + } + .then_some((input, input_utxo)) + }) .collect() } @@ -319,7 +410,16 @@ impl<'a> RandomTxMaker<'a> { .map(|(token_id, _)| AccountType::Token(**token_id)) .collect::>(); - let mut delegations = self + let orders = self + .orders_store + .orders_data() + .iter() + .choose_multiple(rng, number_of_inputs) + .iter() + .map(|(id, _)| AccountType::Order(**id)) + .collect::>(); + + let mut result = self .pos_accounting_store .all_delegation_balances() .iter() @@ -328,10 +428,11 @@ impl<'a> RandomTxMaker<'a> { .map(|(id, _)| AccountType::Delegation(**id)) .collect::>(); - delegations.extend_from_slice(&tokens); - delegations.shuffle(rng); + result.extend_from_slice(&tokens); + result.extend_from_slice(&orders); + result.shuffle(rng); - delegations + result } fn get_next_nonce(&mut self, account: AccountType) -> AccountNonce { @@ -360,6 +461,7 @@ impl<'a> RandomTxMaker<'a> { pos_accounting_before_tx: &impl PoSAccountingView, pos_accounting_latest: &mut (impl PoSAccountingView + PoSAccountingOperations), + orders_cache: &mut (impl OrdersAccountingView + OrdersAccountingOperations), accounts: &[AccountType], key_manager: &mut KeyManager, ) -> (Vec, Vec) { @@ -410,7 +512,50 @@ impl<'a> RandomTxMaker<'a> { result_inputs.extend(inputs); result_outputs.extend(outputs); } - AccountType::Order(_) => unimplemented!(), + AccountType::Order(order_id) => { + if !self.account_command_used { + // cancel an order + let order_data = orders_cache.get_order_data(&order_id).unwrap().unwrap(); + if token_not_frozen(order_data.ask(), tokens_cache) + && token_not_frozen(order_data.give(), tokens_cache) + { + let new_nonce = self.get_next_nonce(AccountType::Order(order_id)); + result_inputs.push(TxInput::AccountCommand( + new_nonce, + AccountCommand::CancelOrder(order_id), + )); + + let available_give_balance = + orders_cache.get_give_balance(&order_id).unwrap().unwrap(); + let give_output = + output_value_with_amount(order_data.give(), available_give_balance); + + let current_ask_balance = + orders_cache.get_ask_balance(&order_id).unwrap().unwrap(); + let filled_amount = (output_value_amount(order_data.ask()) + - current_ask_balance) + .unwrap(); + let filled_output = + output_value_with_amount(order_data.ask(), filled_amount); + + let _ = orders_cache.cancel_order(order_id).unwrap(); + self.account_command_used = true; + + result_outputs.extend(vec![ + TxOutput::Transfer( + give_output, + key_manager + .new_destination(self.chainstate.get_chain_config(), rng), + ), + TxOutput::Transfer( + filled_output, + key_manager + .new_destination(self.chainstate.get_chain_config(), rng), + ), + ]); + } + } + } } } @@ -424,7 +569,7 @@ impl<'a> RandomTxMaker<'a> { token_id: TokenId, key_manager: &mut KeyManager, ) -> (Vec, Vec) { - if !self.account_command_used { + if self.account_command_used || self.fee_input.is_none() { return (Vec::new(), Vec::new()); } @@ -521,7 +666,8 @@ impl<'a> RandomTxMaker<'a> { } }; let supply_left = (supply_limit - circulating_supply).unwrap(); - let to_mint = Amount::from_atoms(rng.gen_range(1..supply_left.into_atoms())); + let mint_limit = std::cmp::min(100_000, supply_left.into_atoms()); + let to_mint = Amount::from_atoms(rng.gen_range(1..=mint_limit)); let new_nonce = self.get_next_nonce(AccountType::Token(token_id)); let account_input = TxInput::AccountCommand( @@ -550,11 +696,13 @@ impl<'a> RandomTxMaker<'a> { (vec![account_input, fee_input], outputs) } else { - let is_locked = match tokens_cache.get_token_data(&token_id).unwrap().unwrap() { - tokens_accounting::TokenData::FungibleToken(data) => data.is_locked(), + let can_be_locked = match tokens_cache.get_token_data(&token_id).unwrap().unwrap() { + tokens_accounting::TokenData::FungibleToken(data) => { + !data.is_locked() && data.try_lock().is_ok() + } }; - if !is_locked { + if can_be_locked { let new_nonce = self.get_next_nonce(AccountType::Token(token_id)); let account_input = TxInput::AccountCommand( new_nonce, @@ -585,12 +733,14 @@ impl<'a> RandomTxMaker<'a> { } /// Given an output as in input creates multiple new random outputs. + #[allow(clippy::too_many_arguments)] fn create_utxo_spending( &mut self, rng: &mut (impl Rng + CryptoRng), staking_pools_observer: &mut impl StakingPoolsObserver, tokens_cache: &mut (impl TokensAccountingView + TokensAccountingOperations), pos_accounting_cache: &mut (impl PoSAccountingView + PoSAccountingOperations), + orders_cache: &mut (impl OrdersAccountingView + OrdersAccountingOperations), key_manager: &mut KeyManager, inputs: Vec<(TxInput, TxOutput)>, ) -> (Vec, Vec) { @@ -603,6 +753,7 @@ impl<'a> RandomTxMaker<'a> { rng, tokens_cache, pos_accounting_cache, + orders_cache, input, v, key_manager, @@ -615,6 +766,7 @@ impl<'a> RandomTxMaker<'a> { rng, tokens_cache, pos_accounting_cache, + orders_cache, input, v, key_manager, @@ -658,6 +810,7 @@ impl<'a> RandomTxMaker<'a> { let (mut new_inputs, new_outputs) = self.spend_tokens_v1( rng, tokens_cache, + orders_cache, *token_id, Amount::from_atoms(1), key_manager, @@ -674,6 +827,7 @@ impl<'a> RandomTxMaker<'a> { rng, tokens_cache, pos_accounting_cache, + orders_cache, input, v, key_manager, @@ -708,11 +862,17 @@ impl<'a> RandomTxMaker<'a> { &mut self, rng: &mut (impl Rng + CryptoRng), coins: Amount, + tokens_cache: &mut (impl TokensAccountingView + TokensAccountingOperations), pos_accounting_cache: &mut (impl PoSAccountingView + PoSAccountingOperations), + orders_cache: &mut (impl OrdersAccountingView + OrdersAccountingOperations), key_manager: &mut KeyManager, - ) -> Vec { + ) -> (Vec, Vec) { + if coins == Amount::ZERO { + return (Vec::new(), Vec::new()); + } + let num_outputs = rng.gen_range(1..5); - let switch = rng.gen_range(0..3); + let switch = rng.gen_range(0..5); if switch == 0 && self.token_can_be_issued { // issue token v1 let min_tx_fee = self.chainstate.get_chain_config().fungible_token_issuance_fee(); @@ -721,7 +881,7 @@ impl<'a> RandomTxMaker<'a> { let change = (coins - min_tx_fee).unwrap(); // Coin output is created intentionally besides issuance output in order to not waste utxo // (e.g. single genesis output on issuance) - vec![ + let outputs = vec![ TxOutput::IssueFungibleToken(Box::new(TokenIssuance::V1( random_token_issuance_v1( self.chainstate.get_chain_config(), @@ -733,9 +893,10 @@ impl<'a> RandomTxMaker<'a> { OutputValue::Coin(change), key_manager.new_destination(self.chainstate.get_chain_config(), rng), ), - ] + ]; + (Vec::new(), outputs) } else { - Vec::new() + (Vec::new(), Vec::new()) } } else if switch == 1 && self.token_can_be_issued { // issue nft v1 @@ -752,7 +913,7 @@ impl<'a> RandomTxMaker<'a> { let dummy_token_id = make_token_id(&dummy_inputs).unwrap(); // Coin output is created intentionally besides issuance output in order to not waste utxo // (e.g. single genesis output on issuance) - vec![ + let outputs = vec![ TxOutput::IssueNft( dummy_token_id, Box::new(NftIssuance::V0(random_nft_issuance( @@ -765,9 +926,72 @@ impl<'a> RandomTxMaker<'a> { OutputValue::Coin(change), key_manager.new_destination(self.chainstate.get_chain_config(), rng), ), - ] + ]; + (Vec::new(), outputs) + } else { + (Vec::new(), Vec::new()) + } + } else if switch == 2 && self.order_can_be_created { + // create order to exchange part of available coins for tokens + if let Some((token_id, token_supply)) = + get_random_token(rng, self.tokens_store, tokens_cache) + { + let ask_amount = + Amount::from_atoms(rng.gen_range(1u128..=token_supply.into_atoms())); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..=coins.into_atoms())); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, ask_amount), + OutputValue::Coin(give_amount), + ); + let change = (coins - give_amount).unwrap(); + + // Transfer output is created intentionally besides order output to not waste utxo + // (e.g. single genesis output on issuance) + let outputs = vec![ + TxOutput::AnyoneCanTake(order_data), + TxOutput::Transfer( + OutputValue::Coin(change), + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + ), + ]; + + self.order_can_be_created = false; + + (Vec::new(), outputs) } else { - Vec::new() + (Vec::new(), Vec::new()) + } + } else if switch == 3 && !self.account_command_used { + // try fill order + let coins_to_use = Amount::from_atoms(rng.gen_range(1u128..=coins.into_atoms())); + if let Some(order_id) = get_random_order_to_fill( + self.orders_store, + &orders_cache, + &OutputValue::Coin(coins_to_use), + ) { + let new_nonce = self.get_next_nonce(AccountType::Order(order_id)); + let inputs = vec![TxInput::AccountCommand( + new_nonce, + AccountCommand::FillOrder( + order_id, + OutputValue::Coin(coins_to_use), + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + ), + )]; + let change = (coins - coins_to_use).unwrap(); + + let _ = orders_cache.fill_order(order_id, OutputValue::Coin(coins_to_use)).unwrap(); + self.account_command_used = true; + + let outputs = vec![TxOutput::Transfer( + OutputValue::Coin(change), + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + )]; + + (inputs, outputs) + } else { + (Vec::new(), Vec::new()) } } else { // transfer coins @@ -861,7 +1085,7 @@ impl<'a> RandomTxMaker<'a> { )); } } - new_outputs + (Vec::new(), new_outputs) } } @@ -871,6 +1095,7 @@ impl<'a> RandomTxMaker<'a> { rng: &mut (impl Rng + CryptoRng), tokens_cache: &mut (impl TokensAccountingView + TokensAccountingOperations), pos_accounting_cache: &mut (impl PoSAccountingView + PoSAccountingOperations), + orders_cache: &mut (impl OrdersAccountingView + OrdersAccountingOperations), input: TxInput, input_utxo_value: &OutputValue, key_manager: &mut KeyManager, @@ -880,7 +1105,15 @@ impl<'a> RandomTxMaker<'a> { match input_utxo_value { OutputValue::Coin(coins) => { - let new_outputs = self.spend_coins(rng, *coins, pos_accounting_cache, key_manager); + let (new_inputs, new_outputs) = self.spend_coins( + rng, + *coins, + tokens_cache, + pos_accounting_cache, + orders_cache, + key_manager, + ); + result_inputs.extend(new_inputs); result_outputs.extend(new_outputs); } OutputValue::TokenV0(_) => { @@ -894,6 +1127,7 @@ impl<'a> RandomTxMaker<'a> { let (new_inputs, new_outputs) = self.spend_tokens_v1( rng, tokens_cache, + orders_cache, *token_id, *amount, key_manager, @@ -912,22 +1146,50 @@ impl<'a> RandomTxMaker<'a> { &mut self, rng: &mut (impl Rng + CryptoRng), tokens_cache: &mut (impl TokensAccountingView + TokensAccountingOperations), + orders_cache: &mut (impl OrdersAccountingView + OrdersAccountingOperations), token_id: TokenId, amount: Amount, key_manager: &mut KeyManager, ) -> (Vec, Vec) { + if amount == Amount::ZERO { + return (Vec::new(), Vec::new()); + } + let atoms_vec = test_utils::split_value(rng, amount.into_atoms()); let mut result_inputs = Vec::new(); let mut result_outputs = Vec::new(); for atoms in atoms_vec { if rng.gen::() { - // transfer - result_outputs.push(TxOutput::Transfer( - OutputValue::TokenV1(token_id, Amount::from_atoms(atoms)), - key_manager.new_destination(self.chainstate.get_chain_config(), rng), - )); - } else if rng.gen_bool(0.9) && !self.account_command_used { + let output_value = OutputValue::TokenV1(token_id, Amount::from_atoms(atoms)); + if rng.gen::() { + // transfer + result_outputs.push(TxOutput::Transfer( + output_value, + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + )); + } else if !self.account_command_used { + // try fill order + if let Some(order_id) = + get_random_order_to_fill(self.orders_store, &orders_cache, &output_value) + { + let new_nonce = self.get_next_nonce(AccountType::Order(order_id)); + + let _ = orders_cache.fill_order(order_id, output_value.clone()).unwrap(); + self.account_command_used = true; + + result_inputs.push(TxInput::AccountCommand( + new_nonce, + AccountCommand::FillOrder( + order_id, + output_value, + key_manager + .new_destination(self.chainstate.get_chain_config(), rng), + ), + )); + } + } + } else if rng.gen_bool(0.4) && !self.account_command_used { // unmint let token_data = tokens_cache.get_token_data(&token_id).unwrap(); @@ -973,6 +1235,20 @@ impl<'a> RandomTxMaker<'a> { self.account_command_used = true; } } + } else if rng.gen_bool(0.4) && self.order_can_be_created && atoms > 0 { + // create order to exchange part of available tokens for coins or other tokens + let random_token = get_random_token(rng, self.tokens_store, tokens_cache); + let ask_value = + if rng.gen::() && random_token.is_some_and(|(id, _)| id != token_id) { + OutputValue::TokenV1(random_token.unwrap().0, random_token.unwrap().1) + } else { + OutputValue::Coin(Amount::from_atoms(rng.gen_range(100..10000))) + }; + + let give_value = OutputValue::TokenV1(token_id, Amount::from_atoms(atoms)); + let order_data = OrderData::new(Destination::AnyoneCanSpend, ask_value, give_value); + result_outputs.push(TxOutput::AnyoneCanTake(order_data)); + self.order_can_be_created = false; } else { // burn let to_burn = Amount::from_atoms(atoms); @@ -993,6 +1269,8 @@ impl<'a> RandomTxMaker<'a> { fn tx_outputs_post_process( rng: &mut (impl Rng + CryptoRng), pos_accounting_cache: &mut (impl PoSAccountingView + PoSAccountingOperations), + tokens_cache: &mut (impl TokensAccountingView + TokensAccountingOperations), + orders_cache: &mut (impl OrdersAccountingView + OrdersAccountingOperations), inputs: &[TxInput], outputs: Vec, ) -> (Vec, BTreeMap) { @@ -1006,7 +1284,6 @@ impl<'a> RandomTxMaker<'a> { | TxOutput::Burn(_) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::DelegateStaking(_, _) - | TxOutput::IssueFungibleToken(_) | TxOutput::DataDeposit(_) | TxOutput::Htlc(_, _) => Some(output), TxOutput::CreateStakePool(dummy_pool_id, pool_data) => { @@ -1046,11 +1323,23 @@ impl<'a> RandomTxMaker<'a> { None // if a pool was decommissioned in this tx then skip creating a delegation } } + TxOutput::IssueFungibleToken(issuance) => { + let token_id = make_token_id(inputs).unwrap(); + let data = tokens_accounting::TokenData::FungibleToken( + issuance.as_ref().clone().into(), + ); + let _ = tokens_cache.issue_token(token_id, data); + Some(output) + } TxOutput::IssueNft(dummy_token_id, _, _) => { *dummy_token_id = make_token_id(inputs).unwrap(); Some(output) } - TxOutput::AnyoneCanTake(_) => unimplemented!(), + TxOutput::AnyoneCanTake(data) => { + let order_id = make_order_id(inputs[0].utxo_outpoint().unwrap()); + let _ = orders_cache.create_order(order_id, data.clone()).unwrap(); + Some(output) + } }) .collect(); @@ -1099,3 +1388,14 @@ impl<'a> RandomTxMaker<'a> { .is_ok() } } + +fn token_not_frozen(value: &OutputValue, view: &impl TokensAccountingView) -> bool { + match value { + OutputValue::Coin(_) | OutputValue::TokenV0(_) => true, + OutputValue::TokenV1(token_id, _) => { + view.get_token_data(token_id).unwrap().is_some_and(|data| match data { + tokens_accounting::TokenData::FungibleToken(data) => !data.is_frozen(), + }) + } + } +} diff --git a/chainstate/test-framework/src/utils.rs b/chainstate/test-framework/src/utils.rs index 122c6ea296..23ccc1cd39 100644 --- a/chainstate/test-framework/src/utils.rs +++ b/chainstate/test-framework/src/utils.rs @@ -76,6 +76,21 @@ pub fn get_output_value(output: &TxOutput) -> Option { } } +pub fn output_value_amount(value: &OutputValue) -> Amount { + match value { + OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => *amount, + OutputValue::TokenV0(_) => panic!("deprecated token version"), + } +} + +pub fn output_value_with_amount(value: &OutputValue, amount: Amount) -> OutputValue { + match value { + OutputValue::Coin(_) => OutputValue::Coin(amount), + OutputValue::TokenV1(token_id, _) => OutputValue::TokenV1(*token_id, amount), + OutputValue::TokenV0(_) => panic!("deprecated token version"), + } +} + pub fn create_new_outputs( srcid: OutPointSourceId, outs: &[TxOutput], diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index 8ae99c1a4c..b15c0b8530 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -15,7 +15,7 @@ use chainstate::ConnectTransactionError; use chainstate_storage::Transactional; -use chainstate_test_framework::{TestFramework, TransactionBuilder}; +use chainstate_test_framework::{output_value_amount, TestFramework, TransactionBuilder}; use common::{ address::pubkeyhash::PublicKeyHash, chain::{ @@ -43,13 +43,6 @@ use tx_verifier::error::{InputCheckError, ScriptError}; use crate::tests::helpers::{issue_token_from_block, mint_tokens_in_block}; -fn output_value_amount(value: &OutputValue) -> Amount { - match value { - OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => *amount, - OutputValue::TokenV0(_) => unreachable!(), - } -} - fn issue_and_mint_token_from_genesis( rng: &mut (impl Rng + CryptoRng), tf: &mut TestFramework, diff --git a/common/src/chain/tokens/issuance.rs b/common/src/chain/tokens/issuance.rs index 05c4b42e44..6c2c8024c3 100644 --- a/common/src/chain/tokens/issuance.rs +++ b/common/src/chain/tokens/issuance.rs @@ -39,7 +39,7 @@ pub enum TokenTotalSupply { Unlimited, // limited only by the Amount data type } -// Indicates whether a token an be frozen +/// Indicates whether a token can be frozen #[derive( Debug, Copy, @@ -69,7 +69,7 @@ impl IsTokenFreezable { } } -// Indicates whether a token an be unfrozen after being frozen +/// Indicates whether a token can be unfrozen after being frozen #[derive( Debug, Copy, @@ -99,9 +99,9 @@ impl IsTokenUnfreezable { } } -// Indicates whether a token is frozen at the moment or not. If it is then no operations wish this token can be performed. -// Meaning transfers, burns, minting, unminting, supply locks etc. Frozen token can only be unfrozen -// is such an option was provided while freezing. +/// Indicates whether a token is frozen at the moment or not. If it is then no operations with this token can be performed. +/// Meaning transfers, burns, minting, unminting, supply locks etc. Frozen token can only be unfrozen +/// is such an option was provided while freezing. #[derive( Debug, Copy, diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index 51a665865e..f68fdae4e5 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -502,6 +502,7 @@ impl MempoolBanScore for orders_accounting::Error { Error::OrderDataNotFound(_) => 0, Error::OrderAskBalanceNotFound(_) => 0, Error::OrderGiveBalanceNotFound(_) => 0, + Error::OrderWithZeroValue(_) => 100, Error::InvariantOrderDataNotFoundForUndo(_) => 100, Error::InvariantOrderAskBalanceNotFoundForUndo(_) => 100, Error::InvariantOrderAskBalanceChangedForUndo(_) => 100, diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 0b6a903295..cc821d932d 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -150,16 +150,24 @@ impl OrdersAccountingView for OrdersAccountingCache

impl OrdersAccountingOperations for OrdersAccountingCache

{ fn create_order(&mut self, id: OrderId, data: OrderData) -> Result { - if self.get_order_data(&id)?.is_some() { - return Err(Error::OrderAlreadyExists(id)); - } + ensure!( + self.get_order_data(&id)?.is_none(), + Error::OrderAlreadyExists(id) + ); - if self.get_ask_balance(&id)?.is_some() { - return Err(Error::OrderAlreadyExists(id)); - } + ensure!( + self.get_ask_balance(&id)?.is_none(), + Error::OrderAlreadyExists(id) + ); let ask_amount = output_value_amount(data.ask())?; let give_amount = output_value_amount(data.give())?; + + ensure!( + ask_amount > Amount::ZERO && give_amount > Amount::ZERO, + Error::OrderWithZeroValue(id) + ); + let undo_data = self .data .order_data diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index 14753ac94b..07049d8cba 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -29,6 +29,8 @@ pub enum Error { OrderAskBalanceNotFound(OrderId), #[error("Give balance for order {0}` not found")] OrderGiveBalanceNotFound(OrderId), + #[error("Attempt to create an order with zero exchange value `{0}`")] + OrderWithZeroValue(OrderId), #[error("Data for order {0}` not found for undo")] InvariantOrderDataNotFoundForUndo(OrderId), #[error("Ask balance for order {0}` not found for undo")] diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index 3aaa79d52f..4c2eaa7ee8 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -274,7 +274,7 @@ fn fill_entire_order_and_flush(#[case] seed: Seed) { assert_eq!(result.unwrap_err(), Error::OrderDataNotFound(random_order)); } - // try to overfill + // try to overbid { let ask_amount = output_value_amount(order_data.ask()).unwrap(); let fill = OutputValue::Coin((ask_amount + Amount::from_atoms(1)).unwrap()); diff --git a/test-utils/src/nft_utils.rs b/test-utils/src/nft_utils.rs index c54d8e099e..b38bf046ec 100644 --- a/test-utils/src/nft_utils.rs +++ b/test-utils/src/nft_utils.rs @@ -56,11 +56,27 @@ pub fn random_token_issuance_v1( let max_dec_count = chain_config.token_max_dec_count(); let max_uri_len = chain_config.token_max_uri_len(); + let _fix_code_below_if_enum_changes = |supply: TokenTotalSupply| match supply { + TokenTotalSupply::Fixed(_) => unreachable!(), + TokenTotalSupply::Lockable => unreachable!(), + TokenTotalSupply::Unlimited => unreachable!(), + }; + + let supply = match rng.gen_range(0..3) { + 0 => { + let supply = Amount::from_atoms(rng.gen_range(1..1_000_000)); + TokenTotalSupply::Fixed(supply) + } + 1 => TokenTotalSupply::Lockable, + 2 => TokenTotalSupply::Unlimited, + _ => unreachable!(), + }; + TokenIssuanceV1 { token_ticker: random_ascii_alphanumeric_string(rng, 1..max_ticker_len).as_bytes().to_vec(), number_of_decimals: rng.gen_range(1..max_dec_count), metadata_uri: random_ascii_alphanumeric_string(rng, 1..max_uri_len).as_bytes().to_vec(), - total_supply: TokenTotalSupply::Lockable, + total_supply: supply, is_freezable: IsTokenFreezable::Yes, authority, } From 53bf14841da5c4d9cd9acefa2dafd85903fb5813 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Fri, 17 May 2024 17:58:39 +0300 Subject: [PATCH 25/37] Disable orders for api-server simulation --- api-server/scanner-lib/src/sync/tests/simulation.rs | 2 +- chainstate/test-framework/src/block_builder.rs | 2 ++ chainstate/test-framework/src/pos_block_builder.rs | 2 ++ chainstate/test-framework/src/random_tx_maker.rs | 6 ++++-- .../test-suite/src/tests/tx_verification_simulation.rs | 4 ++-- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index 16987bedd3..ba10d38ed8 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -240,7 +240,7 @@ async fn simulation( let mut block_builder = tf.make_pos_block_builder().with_random_staking_pool(&mut rng); for _ in 0..rng.gen_range(10..max_tx_per_block) { - block_builder = block_builder.add_test_transaction(&mut rng, false); + block_builder = block_builder.add_test_transaction(&mut rng, false, false); } let block = block_builder.build(&mut rng); diff --git a/chainstate/test-framework/src/block_builder.rs b/chainstate/test-framework/src/block_builder.rs index 08e2cf8d56..ddca73d7c4 100644 --- a/chainstate/test-framework/src/block_builder.rs +++ b/chainstate/test-framework/src/block_builder.rs @@ -139,6 +139,7 @@ impl<'f> BlockBuilder<'f> { mut self, rng: &mut (impl Rng + CryptoRng), support_htlc: bool, + with_orders: bool, ) -> Self { let utxo_set = self .framework @@ -169,6 +170,7 @@ impl<'f> BlockBuilder<'f> { None, account_nonce_getter, support_htlc, + with_orders, ) .make( rng, diff --git a/chainstate/test-framework/src/pos_block_builder.rs b/chainstate/test-framework/src/pos_block_builder.rs index d45d1b8abb..c5cf725e34 100644 --- a/chainstate/test-framework/src/pos_block_builder.rs +++ b/chainstate/test-framework/src/pos_block_builder.rs @@ -379,6 +379,7 @@ impl<'f> PoSBlockBuilder<'f> { mut self, rng: &mut (impl Rng + CryptoRng), support_htlc: bool, + with_orders: bool, ) -> Self { let utxo_set = self .framework @@ -409,6 +410,7 @@ impl<'f> PoSBlockBuilder<'f> { self.staking_pool, account_nonce_getter, support_htlc, + with_orders, ) .make( rng, diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index 804c009ea8..2490fc0fd1 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -205,6 +205,7 @@ pub struct RandomTxMaker<'a> { } impl<'a> RandomTxMaker<'a> { + #[allow(clippy::too_many_arguments)] pub fn new( chainstate: &'a TestChainstate, utxo_set: &'a UtxosDBInMemoryImpl, @@ -213,7 +214,8 @@ impl<'a> RandomTxMaker<'a> { orders_store: &'a InMemoryOrdersAccounting, staking_pool: Option, account_nonce_getter: Box Option + 'a>, - support_htlc: bool, + support_htlc: bool, // TODO: remove this when api-server supports orders + with_orders: bool, // TODO: remove this when api-server supports orders ) -> Self { Self { chainstate, @@ -225,7 +227,7 @@ impl<'a> RandomTxMaker<'a> { account_nonce_getter, account_nonce_tracker: BTreeMap::new(), token_can_be_issued: true, - order_can_be_created: true, + order_can_be_created: with_orders, stake_pool_can_be_created: true, delegation_can_be_created: true, account_command_used: false, diff --git a/chainstate/test-suite/src/tests/tx_verification_simulation.rs b/chainstate/test-suite/src/tests/tx_verification_simulation.rs index e7fe110c3c..fa39b14c0d 100644 --- a/chainstate/test-suite/src/tests/tx_verification_simulation.rs +++ b/chainstate/test-suite/src/tests/tx_verification_simulation.rs @@ -115,7 +115,7 @@ fn simulation(#[case] seed: Seed, #[case] max_blocks: usize, #[case] max_tx_per_ let mut block_builder = tf.make_pos_block_builder().with_random_staking_pool(&mut rng); for _ in 0..rng.gen_range(10..max_tx_per_block) { - block_builder = block_builder.add_test_transaction(&mut rng, true); + block_builder = block_builder.add_test_transaction(&mut rng, true, true); } let block = block_builder.build(&mut rng); @@ -151,7 +151,7 @@ fn simulation(#[case] seed: Seed, #[case] max_blocks: usize, #[case] max_tx_per_ let mut block_builder = tf2.make_pos_block_builder().with_random_staking_pool(&mut rng); for _ in 0..rng.gen_range(10..max_tx_per_block) { - block_builder = block_builder.add_test_transaction(&mut rng, true); + block_builder = block_builder.add_test_transaction(&mut rng, true, true); } let block = block_builder.build(&mut rng); From eeabbd491630354f96d21d061905c7210b9e346c Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Mon, 20 May 2024 16:26:26 +0300 Subject: [PATCH 26/37] Fix mint limit in tests --- chainstate/test-suite/src/tests/orders_tests.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index b15c0b8530..929593513e 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -25,7 +25,7 @@ use common::{ inputsig::{standard_signature::StandardInputSignature, InputWitness}, DestinationSigError, }, - tokens::{TokenId, TokenIssuance}, + tokens::{TokenId, TokenIssuance, TokenTotalSupply}, AccountCommand, AccountNonce, ChainstateUpgrade, Destination, OrderData, SignedTransaction, TxInput, TxOutput, UtxoOutPoint, }, @@ -64,9 +64,21 @@ fn issue_and_mint_token_from_best_block( Destination::AnyoneCanSpend, rng, )); + + let mint_limit = match &issuance { + TokenIssuance::V1(issuance) => match issuance.total_supply { + TokenTotalSupply::Fixed(supply) => supply, + TokenTotalSupply::Lockable | TokenTotalSupply::Unlimited => { + Amount::from_atoms(100_000_000) + } + }, + }; + let (token_id, _, utxo_with_change) = issue_token_from_block(rng, tf, best_block_id, utxo_outpoint, issuance); + let to_mint = Amount::from_atoms(rng.gen_range(1..mint_limit.into_atoms())); + let best_block_id = tf.best_block_id(); let (_, mint_tx_id) = mint_tokens_in_block( rng, @@ -74,7 +86,7 @@ fn issue_and_mint_token_from_best_block( best_block_id, utxo_with_change, token_id, - Amount::from_atoms(100_000), + to_mint, true, ); From 93400fa0622380b0b06dcdb46ab92d01f9d4cc53 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Tue, 21 May 2024 14:41:12 +0300 Subject: [PATCH 27/37] Improve simulation test --- .../scanner-lib/src/sync/tests/simulation.rs | 82 +++- .../test-framework/src/block_builder.rs | 4 +- .../test-framework/src/pos_block_builder.rs | 4 +- .../test-framework/src/random_tx_maker.rs | 448 ++++++++++-------- test-utils/src/nft_utils.rs | 8 +- 5 files changed, 321 insertions(+), 225 deletions(-) diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index ba10d38ed8..d00744e6fb 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -31,7 +31,7 @@ use api_server_common::storage::{ }, }; -use chainstate::{BlockSource, ChainstateConfig}; +use chainstate::{chainstate_interface::ChainstateInterface, BlockSource, ChainstateConfig}; use chainstate_test_framework::TestFramework; use common::{ chain::{ @@ -49,11 +49,70 @@ use crypto::{ key::{KeyKind, PrivateKey}, vrf::{VRFKeyKind, VRFPrivateKey}, }; -use pos_accounting::make_delegation_id; +use pos_accounting::{make_delegation_id, PoSAccountingView}; use randomness::Rng; use rstest::rstest; use test_utils::random::{make_seedable_rng, Seed}; +struct PoSAccountingAdapterToCheckFees<'a> { + chainstate: &'a dyn ChainstateInterface, +} + +impl<'a> PoSAccountingAdapterToCheckFees<'a> { + pub fn new(chainstate: &'a dyn ChainstateInterface) -> Self { + Self { chainstate } + } +} + +impl<'a> PoSAccountingView for PoSAccountingAdapterToCheckFees<'a> { + type Error = pos_accounting::Error; + + fn pool_exists(&self, _pool_id: PoolId) -> Result { + unimplemented!() + } + + fn get_pool_balance(&self, _pool_id: PoolId) -> Result, Self::Error> { + unimplemented!() + } + + fn get_pool_data( + &self, + pool_id: PoolId, + ) -> Result, Self::Error> { + Ok(self.chainstate.get_stake_pool_data(pool_id).unwrap()) + } + + fn get_pool_delegations_shares( + &self, + _pool_id: PoolId, + ) -> Result>, Self::Error> { + unimplemented!() + } + + fn get_delegation_balance( + &self, + _delegation_id: DelegationId, + ) -> Result, Self::Error> { + // only used for checks for attempted to print money but we don't need to check that here + Ok(Some(Amount::MAX)) + } + + fn get_delegation_data( + &self, + _delegation_id: DelegationId, + ) -> Result, Self::Error> { + unimplemented!() + } + + fn get_pool_delegation_share( + &self, + _pool_id: PoolId, + _delegation_id: DelegationId, + ) -> Result, Self::Error> { + unimplemented!() + } +} + #[rstest] #[trace] #[case(test_utils::random::Seed::from_entropy(), 20, 50)] @@ -425,28 +484,13 @@ async fn simulation( }) .collect(); - //let staker_balance_getter = |pool_id: PoolId| { - // Ok(Some( - // tf.chainstate - // .get_stake_pool_data(pool_id) - // .unwrap() - // .unwrap() - // .staker_balance() - // .unwrap(), - // )) - //}; - // only used for checks for attempted to print money but we don't need to check that here - //let delegation_balance_getter = - // |_delegation_id: DelegationId| Ok(Some(Amount::MAX)); - // FIXME: proper impl - let pos_store = pos_accounting::InMemoryPoSAccounting::new(); - let pos_db = pos_accounting::PoSAccountingDB::new(&pos_store); + let pos_store = PoSAccountingAdapterToCheckFees::new(tf.chainstate.as_ref()); let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, &orders_db, - &pos_db, + &pos_store, tx.inputs(), &inputs_utxos, ) diff --git a/chainstate/test-framework/src/block_builder.rs b/chainstate/test-framework/src/block_builder.rs index ddca73d7c4..cfcd7b8027 100644 --- a/chainstate/test-framework/src/block_builder.rs +++ b/chainstate/test-framework/src/block_builder.rs @@ -139,7 +139,7 @@ impl<'f> BlockBuilder<'f> { mut self, rng: &mut (impl Rng + CryptoRng), support_htlc: bool, - with_orders: bool, + support_orders: bool, ) -> Self { let utxo_set = self .framework @@ -170,7 +170,7 @@ impl<'f> BlockBuilder<'f> { None, account_nonce_getter, support_htlc, - with_orders, + support_orders, ) .make( rng, diff --git a/chainstate/test-framework/src/pos_block_builder.rs b/chainstate/test-framework/src/pos_block_builder.rs index c5cf725e34..72ad0f7af0 100644 --- a/chainstate/test-framework/src/pos_block_builder.rs +++ b/chainstate/test-framework/src/pos_block_builder.rs @@ -379,7 +379,7 @@ impl<'f> PoSBlockBuilder<'f> { mut self, rng: &mut (impl Rng + CryptoRng), support_htlc: bool, - with_orders: bool, + support_orders: bool, ) -> Self { let utxo_set = self .framework @@ -410,7 +410,7 @@ impl<'f> PoSBlockBuilder<'f> { self.staking_pool, account_nonce_getter, support_htlc, - with_orders, + support_orders, ) .make( rng, diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index 2490fc0fd1..ffd917ca12 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -214,8 +214,8 @@ impl<'a> RandomTxMaker<'a> { orders_store: &'a InMemoryOrdersAccounting, staking_pool: Option, account_nonce_getter: Box Option + 'a>, - support_htlc: bool, // TODO: remove this when api-server supports orders - with_orders: bool, // TODO: remove this when api-server supports orders + support_htlc: bool, // TODO: remove this when api-server supports orders + support_orders: bool, // TODO: remove this when api-server supports orders ) -> Self { Self { chainstate, @@ -227,7 +227,7 @@ impl<'a> RandomTxMaker<'a> { account_nonce_getter, account_nonce_tracker: BTreeMap::new(), token_can_be_issued: true, - order_can_be_created: with_orders, + order_can_be_created: support_orders, stake_pool_can_be_created: true, delegation_can_be_created: true, account_command_used: false, @@ -873,222 +873,247 @@ impl<'a> RandomTxMaker<'a> { return (Vec::new(), Vec::new()); } - let num_outputs = rng.gen_range(1..5); - let switch = rng.gen_range(0..5); - if switch == 0 && self.token_can_be_issued { - // issue token v1 - let min_tx_fee = self.chainstate.get_chain_config().fungible_token_issuance_fee(); - if coins >= min_tx_fee { - self.token_can_be_issued = false; - let change = (coins - min_tx_fee).unwrap(); - // Coin output is created intentionally besides issuance output in order to not waste utxo - // (e.g. single genesis output on issuance) - let outputs = vec![ - TxOutput::IssueFungibleToken(Box::new(TokenIssuance::V1( - random_token_issuance_v1( - self.chainstate.get_chain_config(), + let atoms_vec = test_utils::split_value(rng, coins.into_atoms()) + .into_iter() + .filter(|v| *v > 0) + .collect::>(); + + let mut result_inputs = Vec::new(); + let mut result_outputs = Vec::new(); + + for atoms_to_spend in atoms_vec { + let switch = rng.gen_range(0..6); + let amount_to_spend = Amount::from_atoms(atoms_to_spend); + if switch == 0 && self.token_can_be_issued { + // issue token v1 + let min_tx_fee = self.chainstate.get_chain_config().fungible_token_issuance_fee(); + if amount_to_spend >= min_tx_fee { + self.token_can_be_issued = false; + let change = (amount_to_spend - min_tx_fee).unwrap(); + // Coin output is created intentionally besides issuance output in order to not waste utxo + // (e.g. single genesis output on issuance) + let outputs = vec![ + TxOutput::IssueFungibleToken(Box::new(TokenIssuance::V1( + random_token_issuance_v1( + self.chainstate.get_chain_config(), + key_manager + .new_destination(self.chainstate.get_chain_config(), rng), + rng, + ), + ))), + TxOutput::Transfer( + OutputValue::Coin(change), key_manager.new_destination(self.chainstate.get_chain_config(), rng), - rng, ), - ))), - TxOutput::Transfer( - OutputValue::Coin(change), - key_manager.new_destination(self.chainstate.get_chain_config(), rng), - ), - ]; - (Vec::new(), outputs) - } else { - (Vec::new(), Vec::new()) - } - } else if switch == 1 && self.token_can_be_issued { - // issue nft v1 - let min_tx_fee = - self.chainstate.get_chain_config().nft_issuance_fee(BlockHeight::zero()); - if coins >= min_tx_fee { - self.token_can_be_issued = false; - let change = (coins - min_tx_fee).unwrap(); - - let dummy_inputs = vec![TxInput::from_utxo( - OutPointSourceId::Transaction(Id::::new(H256::zero())), - 0, - )]; - let dummy_token_id = make_token_id(&dummy_inputs).unwrap(); - // Coin output is created intentionally besides issuance output in order to not waste utxo - // (e.g. single genesis output on issuance) - let outputs = vec![ - TxOutput::IssueNft( - dummy_token_id, - Box::new(NftIssuance::V0(random_nft_issuance( - self.chainstate.get_chain_config(), - rng, - ))), - key_manager.new_destination(self.chainstate.get_chain_config(), rng), - ), - TxOutput::Transfer( - OutputValue::Coin(change), - key_manager.new_destination(self.chainstate.get_chain_config(), rng), - ), - ]; - (Vec::new(), outputs) - } else { - (Vec::new(), Vec::new()) - } - } else if switch == 2 && self.order_can_be_created { - // create order to exchange part of available coins for tokens - if let Some((token_id, token_supply)) = - get_random_token(rng, self.tokens_store, tokens_cache) - { - let ask_amount = - Amount::from_atoms(rng.gen_range(1u128..=token_supply.into_atoms())); - let give_amount = Amount::from_atoms(rng.gen_range(1u128..=coins.into_atoms())); - let order_data = OrderData::new( - Destination::AnyoneCanSpend, - OutputValue::TokenV1(token_id, ask_amount), - OutputValue::Coin(give_amount), - ); - let change = (coins - give_amount).unwrap(); + ]; - // Transfer output is created intentionally besides order output to not waste utxo - // (e.g. single genesis output on issuance) - let outputs = vec![ - TxOutput::AnyoneCanTake(order_data), - TxOutput::Transfer( - OutputValue::Coin(change), - key_manager.new_destination(self.chainstate.get_chain_config(), rng), - ), - ]; + result_outputs.extend_from_slice(&outputs); + } + } else if switch == 1 && self.token_can_be_issued { + // issue nft v1 + let min_tx_fee = + self.chainstate.get_chain_config().nft_issuance_fee(BlockHeight::zero()); + if amount_to_spend >= min_tx_fee { + self.token_can_be_issued = false; + let change = (amount_to_spend - min_tx_fee).unwrap(); + + let dummy_inputs = vec![TxInput::from_utxo( + OutPointSourceId::Transaction(Id::::new(H256::zero())), + 0, + )]; + let dummy_token_id = make_token_id(&dummy_inputs).unwrap(); + // Coin output is created intentionally besides issuance output in order to not waste utxo + // (e.g. single genesis output on issuance) + let outputs = vec![ + TxOutput::IssueNft( + dummy_token_id, + Box::new(NftIssuance::V0(random_nft_issuance( + self.chainstate.get_chain_config(), + rng, + ))), + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + ), + TxOutput::Transfer( + OutputValue::Coin(change), + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + ), + ]; - self.order_can_be_created = false; + result_outputs.extend_from_slice(&outputs); + } + } else if switch == 2 && self.order_can_be_created { + // create order to exchange part of available coins for tokens + if let Some((token_id, token_supply)) = + get_random_token(rng, self.tokens_store, tokens_cache) + { + let ask_amount = + Amount::from_atoms(rng.gen_range(1u128..=token_supply.into_atoms())); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..=atoms_to_spend)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::TokenV1(token_id, ask_amount), + OutputValue::Coin(give_amount), + ); + let change = (amount_to_spend - give_amount).unwrap(); + + // Transfer output is created intentionally besides order output to not waste utxo + // (e.g. single genesis output on issuance) + let outputs = vec![ + TxOutput::AnyoneCanTake(order_data), + TxOutput::Transfer( + OutputValue::Coin(change), + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + ), + ]; - (Vec::new(), outputs) - } else { - (Vec::new(), Vec::new()) - } - } else if switch == 3 && !self.account_command_used { - // try fill order - let coins_to_use = Amount::from_atoms(rng.gen_range(1u128..=coins.into_atoms())); - if let Some(order_id) = get_random_order_to_fill( - self.orders_store, - &orders_cache, - &OutputValue::Coin(coins_to_use), - ) { - let new_nonce = self.get_next_nonce(AccountType::Order(order_id)); - let inputs = vec![TxInput::AccountCommand( - new_nonce, - AccountCommand::FillOrder( - order_id, - OutputValue::Coin(coins_to_use), - key_manager.new_destination(self.chainstate.get_chain_config(), rng), - ), - )]; - let change = (coins - coins_to_use).unwrap(); + self.order_can_be_created = false; - let _ = orders_cache.fill_order(order_id, OutputValue::Coin(coins_to_use)).unwrap(); - self.account_command_used = true; + result_outputs.extend_from_slice(&outputs); + } + } else if switch == 3 && !self.account_command_used { + // try fill order + let fill_value = OutputValue::Coin(amount_to_spend); + if let Some(order_id) = + get_random_order_to_fill(self.orders_store, &orders_cache, &fill_value) + { + let filled_value = + calculate_filled_order_value(&orders_cache, order_id, &fill_value); + + if token_not_frozen(&filled_value, tokens_cache) { + let new_nonce = self.get_next_nonce(AccountType::Order(order_id)); + let input = TxInput::AccountCommand( + new_nonce, + AccountCommand::FillOrder( + order_id, + fill_value.clone(), + key_manager + .new_destination(self.chainstate.get_chain_config(), rng), + ), + ); - let outputs = vec![TxOutput::Transfer( - OutputValue::Coin(change), - key_manager.new_destination(self.chainstate.get_chain_config(), rng), - )]; + let output = TxOutput::Transfer( + filled_value, + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + ); - (inputs, outputs) - } else { - (Vec::new(), Vec::new()) - } - } else { - // transfer coins - let num_outputs = if num_outputs > coins.into_atoms() { - 1 - } else { - num_outputs - }; + let _ = orders_cache.fill_order(order_id, fill_value).unwrap(); + self.account_command_used = true; - let mut new_outputs = (0..num_outputs) - .map(|_| { - let new_value = Amount::from_atoms(coins.into_atoms() / num_outputs); - debug_assert!(new_value >= Amount::from_atoms(1)); + result_inputs.push(input); + result_outputs.push(output); + } + } + } else if switch == 6 { + // data deposit + let min_tx_fee = self.chainstate.get_chain_config().data_deposit_fee(); + if amount_to_spend >= min_tx_fee { + let change = (amount_to_spend - min_tx_fee).unwrap(); + + let deposited_data_len = + self.chainstate.get_chain_config().data_deposit_max_size(); + let deposited_data_len = rng.gen_range(0..deposited_data_len); + let deposited_data = + (0..deposited_data_len).map(|_| rng.gen::()).collect::>(); + + let outputs = vec![ + TxOutput::DataDeposit(deposited_data), + TxOutput::Transfer( + OutputValue::Coin(change), + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + ), + ]; - if new_value >= self.chainstate.get_chain_config().min_stake_pool_pledge() - && self.stake_pool_can_be_created - { - // If enough coins for pledge - create a pool - let dummy_pool_id = PoolId::new(H256::zero()); - self.stake_pool_can_be_created = false; + result_outputs.extend_from_slice(&outputs); + } + } else { + // transfer coins + let output = if amount_to_spend + >= self.chainstate.get_chain_config().min_stake_pool_pledge() + && self.stake_pool_can_be_created + { + // If enough coins for pledge - create a pool + let dummy_pool_id = PoolId::new(H256::zero()); + self.stake_pool_can_be_created = false; - let pool_data = StakePoolData::new( - new_value, - Destination::AnyoneCanSpend, - VRFPrivateKey::new_from_rng(rng, VRFKeyKind::Schnorrkel).1, - Destination::AnyoneCanSpend, - PerThousand::new_from_rng(rng), - Amount::from_atoms(rng.gen_range(0..1000)), - ); - TxOutput::CreateStakePool(dummy_pool_id, Box::new(pool_data)) - } else { - if rng.gen_bool(0.3) { - // Send coins to random delegation - if let Some((delegation_id, _)) = get_random_delegation_data( - rng, - self.pos_accounting_store, - &pos_accounting_cache, - ) { - let _ = pos_accounting_cache - .delegate_staking(*delegation_id, new_value) - .unwrap(); - return TxOutput::DelegateStaking(new_value, *delegation_id); - } + let pool_data = StakePoolData::new( + amount_to_spend, + Destination::AnyoneCanSpend, + VRFPrivateKey::new_from_rng(rng, VRFKeyKind::Schnorrkel).1, + Destination::AnyoneCanSpend, + PerThousand::new_from_rng(rng), + Amount::from_atoms(rng.gen_range(0..1000)), + ); + + TxOutput::CreateStakePool(dummy_pool_id, Box::new(pool_data)) + } else { + if rng.gen_bool(0.3) { + // Send coins to random delegation + if let Some((delegation_id, _)) = get_random_delegation_data( + rng, + self.pos_accounting_store, + &pos_accounting_cache, + ) { + let _ = pos_accounting_cache + .delegate_staking(*delegation_id, amount_to_spend) + .unwrap(); + result_outputs + .push(TxOutput::DelegateStaking(amount_to_spend, *delegation_id)); + continue; } + } - let destination = - key_manager.new_destination(self.chainstate.get_chain_config(), rng); - let timelock = get_random_timelock(rng, self.chainstate); - match rng.gen_range(0..5) { - 0 => TxOutput::LockThenTransfer( - OutputValue::Coin(new_value), - destination, - timelock, - ), - 1 => { - if self.support_htlc { - TxOutput::Htlc( - OutputValue::Coin(new_value), - Box::new(HashedTimelockContract { - secret_hash: HtlcSecretHash::zero(), - spend_key: destination, - refund_timelock: timelock, - refund_key: key_manager - .new_2_of_2_multisig_destination( - self.chainstate.get_chain_config(), - rng, - ), - }), - ) - } else { - TxOutput::Transfer(OutputValue::Coin(new_value), destination) - } + let destination = + key_manager.new_destination(self.chainstate.get_chain_config(), rng); + let timelock = get_random_timelock(rng, self.chainstate); + match rng.gen_range(0..5) { + 0 => TxOutput::LockThenTransfer( + OutputValue::Coin(amount_to_spend), + destination, + timelock, + ), + 1 => { + if self.support_htlc { + TxOutput::Htlc( + OutputValue::Coin(amount_to_spend), + Box::new(HashedTimelockContract { + secret_hash: HtlcSecretHash::zero(), + spend_key: destination, + refund_timelock: timelock, + refund_key: key_manager.new_2_of_2_multisig_destination( + self.chainstate.get_chain_config(), + rng, + ), + }), + ) + } else { + TxOutput::Transfer(OutputValue::Coin(amount_to_spend), destination) } - 2..=4 => TxOutput::Transfer(OutputValue::Coin(new_value), destination), - _ => unreachable!(), } + 2..=4 => { + TxOutput::Transfer(OutputValue::Coin(amount_to_spend), destination) + } + _ => unreachable!(), } - }) - .collect::>(); + }; - // Occasionally create new delegation id - if rng.gen_bool(0.3) && self.delegation_can_be_created { - if let Some((pool_id, _)) = - get_random_pool_data(rng, self.pos_accounting_store, &pos_accounting_cache) - { - self.delegation_can_be_created = false; + result_outputs.push(output); - new_outputs.push(TxOutput::CreateDelegationId( - key_manager.new_destination(self.chainstate.get_chain_config(), rng), - *pool_id, - )); + // Occasionally create new delegation id + if rng.gen::() && self.delegation_can_be_created { + if let Some((pool_id, _)) = + get_random_pool_data(rng, self.pos_accounting_store, &pos_accounting_cache) + { + self.delegation_can_be_created = false; + + result_outputs.push(TxOutput::CreateDelegationId( + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + *pool_id, + )); + } } } - (Vec::new(), new_outputs) } + (result_inputs, result_outputs) } #[allow(clippy::too_many_arguments)] @@ -1177,18 +1202,26 @@ impl<'a> RandomTxMaker<'a> { { let new_nonce = self.get_next_nonce(AccountType::Order(order_id)); - let _ = orders_cache.fill_order(order_id, output_value.clone()).unwrap(); - self.account_command_used = true; + let filled_value = + calculate_filled_order_value(&orders_cache, order_id, &output_value); + + result_outputs.push(TxOutput::Transfer( + filled_value, + key_manager.new_destination(self.chainstate.get_chain_config(), rng), + )); result_inputs.push(TxInput::AccountCommand( new_nonce, AccountCommand::FillOrder( order_id, - output_value, + output_value.clone(), key_manager .new_destination(self.chainstate.get_chain_config(), rng), ), )); + + let _ = orders_cache.fill_order(order_id, output_value).unwrap(); + self.account_command_used = true; } } } else if rng.gen_bool(0.4) && !self.account_command_used { @@ -1401,3 +1434,16 @@ fn token_not_frozen(value: &OutputValue, view: &impl TokensAccountingView) -> bo } } } + +fn calculate_filled_order_value( + view: &impl OrdersAccountingView, + order_id: OrderId, + fill_value: &OutputValue, +) -> OutputValue { + let order_data = view.get_order_data(&order_id).unwrap().unwrap(); + + let filled_amount = + orders_accounting::calculate_fill_order(view, order_id, fill_value).unwrap(); + + output_value_with_amount(order_data.give(), filled_amount) +} diff --git a/test-utils/src/nft_utils.rs b/test-utils/src/nft_utils.rs index b38bf046ec..7e3f8ee58d 100644 --- a/test-utils/src/nft_utils.rs +++ b/test-utils/src/nft_utils.rs @@ -72,12 +72,18 @@ pub fn random_token_issuance_v1( _ => unreachable!(), }; + let is_freezable = if rng.gen::() { + IsTokenFreezable::Yes + } else { + IsTokenFreezable::No + }; + TokenIssuanceV1 { token_ticker: random_ascii_alphanumeric_string(rng, 1..max_ticker_len).as_bytes().to_vec(), number_of_decimals: rng.gen_range(1..max_dec_count), metadata_uri: random_ascii_alphanumeric_string(rng, 1..max_uri_len).as_bytes().to_vec(), total_supply: supply, - is_freezable: IsTokenFreezable::Yes, + is_freezable, authority, } } From 2d9c9e7ab1c689d01013124a81fd81a168c5a0e0 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Fri, 21 Jun 2024 15:22:23 +0300 Subject: [PATCH 28/37] WIP: tranlate to mintscript --- Cargo.lock | 1 + chainstate/src/detail/ban_score.rs | 4 +- chainstate/src/detail/error_classification.rs | 4 +- .../transaction_verifier/input_check/mod.rs | 50 +++++++++++++------ .../src/transaction_verifier/mod.rs | 7 ++- mempool/src/error/ban_score.rs | 4 +- mintscript/Cargo.toml | 1 + mintscript/src/tests/translate/mocks.rs | 24 +++++++++ mintscript/src/translate.rs | 21 ++++++-- 9 files changed, 94 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b77c20ade..0dc1dad924 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4208,6 +4208,7 @@ dependencies = [ "crypto", "expect-test", "hex", + "orders-accounting", "pos-accounting", "rstest", "serialization", diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index d2c30b83cf..fbfc503e33 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -172,11 +172,13 @@ impl BanScore for mintscript::translate::TranslationError { | Self::IllegalOutputSpend | Self::PoolNotFound(_) | Self::DelegationNotFound(_) - | Self::TokenNotFound(_) => 100, + | Self::TokenNotFound(_) + | Self::OrderNotFound(_) => 100, Self::SignatureError(_) => 100, Self::PoSAccounting(e) => e.ban_score(), Self::TokensAccounting(e) => e.ban_score(), + Self::OrdersAccounting(e) => e.ban_score(), } } } diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index c2e84a1dfc..774813c3a2 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -348,10 +348,12 @@ impl BlockProcessingErrorClassification for mintscript::translate::TranslationEr | Self::IllegalOutputSpend | Self::PoolNotFound(_) | Self::DelegationNotFound(_) - | Self::TokenNotFound(_) => BlockProcessingErrorClass::BadBlock, + | Self::TokenNotFound(_) + | Self::OrderNotFound(_) => BlockProcessingErrorClass::BadBlock, Self::PoSAccounting(e) => e.classify(), Self::TokensAccounting(e) => e.classify(), + Self::OrdersAccounting(e) => e.classify(), Self::SignatureError(e) => e.classify(), } } diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_check/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/input_check/mod.rs index 2126a46af3..4880ff1f7e 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_check/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_check/mod.rs @@ -132,26 +132,35 @@ impl mintscript::translate::InputInfoProvider for PerInputData<'_> { } } -pub struct TranslationContextFull<'a, AV, TV> { +pub struct TranslationContextFull<'a, AV, TV, OV> { // Sources of additional information, should it be required. pos_accounting: AV, tokens_accounting: TV, + orders_accounting: OV, // Information about the input input: &'a PerInputData<'a>, } -impl<'a, AV, TV> TranslationContextFull<'a, AV, TV> { - fn new(pos_accounting: AV, tokens_accounting: TV, input: &'a PerInputData<'a>) -> Self { +impl<'a, AV, TV, OV> TranslationContextFull<'a, AV, TV, OV> { + fn new( + pos_accounting: AV, + tokens_accounting: TV, + orders_accounting: OV, + input: &'a PerInputData<'a>, + ) -> Self { Self { pos_accounting, tokens_accounting, + orders_accounting, input, } } } -impl mintscript::translate::InputInfoProvider for TranslationContextFull<'_, AV, TV> { +impl mintscript::translate::InputInfoProvider + for TranslationContextFull<'_, AV, TV, OV> +{ fn input_info(&self) -> &InputInfo { self.input.input_info() } @@ -161,13 +170,16 @@ impl mintscript::translate::InputInfoProvider for TranslationContextFull } } -impl mintscript::translate::SignatureInfoProvider for TranslationContextFull<'_, AV, TV> +impl mintscript::translate::SignatureInfoProvider + for TranslationContextFull<'_, AV, TV, OV> where AV: pos_accounting::PoSAccountingView, TV: tokens_accounting::TokensAccountingView, + OV: orders_accounting::OrdersAccountingView, { type PoSAccounting = AV; type Tokens = TV; + type Orders = OV; fn pos_accounting(&self) -> &Self::PoSAccounting { &self.pos_accounting @@ -176,12 +188,17 @@ where fn tokens(&self) -> &Self::Tokens { &self.tokens_accounting } + + fn orders(&self) -> &Self::Orders { + &self.orders_accounting + } } -impl TranslationContextFull<'_, AV, TV> +impl TranslationContextFull<'_, AV, TV, OV> where AV: pos_accounting::PoSAccountingView, TV: tokens_accounting::TokensAccountingView, + OV: orders_accounting::OrdersAccountingView, { fn to_script>(&self) -> Result { Ok(T::translate_input(self)?) @@ -439,34 +456,36 @@ impl SignatureContext for InputVerifyContextFull<'_, T, S> { } } -pub trait FullyVerifiable: - Transactable + for<'a> TranslateInput> +pub trait FullyVerifiable: + Transactable + for<'a> TranslateInput> { } -impl FullyVerifiable for T where - T: Transactable + for<'a> TranslateInput> +impl FullyVerifiable for T where + T: Transactable + for<'a> TranslateInput> { } /// Perform full verification of given input. #[allow(clippy::too_many_arguments)] -pub fn verify_full( +pub fn verify_full( transaction: &T, chain_config: &ChainConfig, utxos_view: &UV, pos_accounting: &AV, tokens_accounting: &TV, + orders_accounting: &OV, storage: &S, tx_source: &TransactionSourceForConnect, spending_time: BlockTimestamp, ) -> Result<(), InputCheckError> where - T: FullyVerifiable, + T: FullyVerifiable, S: TransactionVerifierStorageRef, UV: utxo::UtxosView, AV: pos_accounting::PoSAccountingView, TV: tokens_accounting::TokensAccountingView, + OV: orders_accounting::OrdersAccountingView, { let core_ctx = CoreContext::new(utxos_view, transaction)?; let tl_ctx = VerifyContextTimelock::for_verifier( @@ -479,9 +498,10 @@ where let ctx = VerifyContextFull::new(transaction, &tl_ctx); for (n, inp) in core_ctx.inputs_iter() { - let script = TranslationContextFull::new(pos_accounting, tokens_accounting, inp) - .to_script::() - .map_err(|e| InputCheckError::new(n, e))?; + let script = + TranslationContextFull::new(pos_accounting, tokens_accounting, orders_accounting, inp) + .to_script::() + .map_err(|e| InputCheckError::new(n, e))?; let mut checker = mintscript::ScriptChecker::full(InputVerifyContextFull::new(&ctx, n)); script.verify(&mut checker).map_err(|e| InputCheckError::new(n, e))?; } diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 1c534f19e4..61ecbfeba1 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -1103,7 +1103,11 @@ where median_time_past: BlockTimestamp, ) -> Result<(), input_check::InputCheckError> where - Tx: input_check::FullyVerifiable, TokensAccountingCache>, + Tx: input_check::FullyVerifiable< + PoSAccountingDelta, + TokensAccountingCache, + OrdersAccountingCache, + >, { input_check::verify_full( tx, @@ -1111,6 +1115,7 @@ where &self.utxo_cache, self.pos_accounting_adapter.accounting_delta(), &self.tokens_accounting_cache, + &self.orders_accounting_cache, &self.storage, tx_source, median_time_past, diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index f68fdae4e5..99eb8eec8f 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -196,11 +196,13 @@ impl MempoolBanScore for mintscript::translate::TranslationError { | Self::IllegalOutputSpend | Self::PoolNotFound(_) | Self::DelegationNotFound(_) - | Self::TokenNotFound(_) => 100, + | Self::TokenNotFound(_) + | Self::OrderNotFound(_) => 100, Self::SignatureError(_) => 100, Self::PoSAccounting(e) => e.ban_score(), Self::TokensAccounting(e) => e.ban_score(), + Self::OrdersAccounting(e) => e.ban_score(), } } } diff --git a/mintscript/Cargo.toml b/mintscript/Cargo.toml index 2d3b1c0abe..2b3f315905 100644 --- a/mintscript/Cargo.toml +++ b/mintscript/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true [dependencies] common = { path = "../common" } crypto = { path = "../crypto" } +orders-accounting = { path = "../orders-accounting" } pos-accounting = { path = "../pos-accounting" } serialization = { path = "../serialization" } tokens-accounting = { path = "../tokens-accounting" } diff --git a/mintscript/src/tests/translate/mocks.rs b/mintscript/src/tests/translate/mocks.rs index 2ee11b4098..4979211a01 100644 --- a/mintscript/src/tests/translate/mocks.rs +++ b/mintscript/src/tests/translate/mocks.rs @@ -60,6 +60,7 @@ impl crate::translate::InputInfoProvider for MockSigInfoProvider<'_> { impl crate::translate::SignatureInfoProvider for MockSigInfoProvider<'_> { type PoSAccounting = Self; type Tokens = Self; + type Orders = Self; fn pos_accounting(&self) -> &Self::PoSAccounting { self @@ -68,6 +69,10 @@ impl crate::translate::SignatureInfoProvider for MockSigInfoProvider<'_> { fn tokens(&self) -> &Self::Tokens { self } + + fn orders(&self) -> &Self::Orders { + self + } } impl pos_accounting::PoSAccountingView for MockSigInfoProvider<'_> { @@ -120,3 +125,22 @@ impl tokens_accounting::TokensAccountingView for MockSigInfoProvider<'_> { unreachable!("not used in these tests") } } + +impl orders_accounting::OrdersAccountingView for MockSigInfoProvider<'_> { + type Error = orders_accounting::Error; + + fn get_order_data( + &self, + id: &common::chain::OrderId, + ) -> Result, Self::Error> { + todo!() + } + + fn get_ask_balance(&self, id: &common::chain::OrderId) -> Result, Self::Error> { + todo!() + } + + fn get_give_balance(&self, id: &common::chain::OrderId) -> Result, Self::Error> { + todo!() + } +} diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index c944217380..3480a17870 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -23,9 +23,10 @@ use common::chain::{ DestinationSigError, }, tokens::TokenId, - AccountCommand, AccountOutPoint, AccountSpending, DelegationId, Destination, PoolId, + AccountCommand, AccountOutPoint, AccountSpending, DelegationId, Destination, OrderId, PoolId, SignedTransaction, TxOutput, UtxoOutPoint, }; +use orders_accounting::OrdersAccountingView; use pos_accounting::PoSAccountingView; use tokens_accounting::TokensAccountingView; use utxo::Utxo; @@ -50,6 +51,9 @@ pub enum TranslationError { #[error(transparent)] TokensAccounting(#[from] tokens_accounting::Error), + #[error(transparent)] + OrdersAccounting(#[from] orders_accounting::Error), + #[error(transparent)] SignatureError(#[from] DestinationSigError), @@ -61,6 +65,9 @@ pub enum TranslationError { #[error("Token with id {0} does not exist")] TokenNotFound(TokenId), + + #[error("Order with id {0} does not exist")] + OrderNotFound(OrderId), } /// Contextual information about given input @@ -95,9 +102,11 @@ pub trait InputInfoProvider { pub trait SignatureInfoProvider: InputInfoProvider { type PoSAccounting: PoSAccountingView; type Tokens: TokensAccountingView; + type Orders: OrdersAccountingView; fn pos_accounting(&self) -> &Self::PoSAccounting; fn tokens(&self) -> &Self::Tokens; + fn orders(&self) -> &Self::Orders; } pub trait TranslateInput { @@ -206,8 +215,14 @@ impl TranslateInput for SignedTransaction { }; Ok(checksig(dest)) } - AccountCommand::CancelOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + AccountCommand::CancelOrder(order_id) => { + let order_data = ctx + .orders() + .get_order_data(order_id)? + .ok_or(TranslationError::OrderNotFound(*order_id))?; + Ok(checksig(order_data.cancel_key())) + } + AccountCommand::FillOrder(_, _, _) => Ok(WitnessScript::TRUE), }, } } From 3c8de657041f4ac6c0ae0fd7f2a5a7eac35660e6 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 23 Jun 2024 13:06:34 +0300 Subject: [PATCH 29/37] Add translation tests --- mintscript/src/tests/translate/mocks.rs | 19 ++++----- mintscript/src/tests/translate/mod.rs | 39 ++++++++++++++++++- ...snap.translate.reward.anyonecantake_00.txt | 1 + ...snap.translate.reward.anyonecantake_01.txt | 1 + .../snap.translate.reward.cancelorder_00.txt | 1 + .../snap.translate.reward.cancelorder_01.txt | 1 + .../snap.translate.reward.cancelorder_02.txt | 1 + .../snap.translate.reward.cancelorder_03.txt | 1 + .../snap.translate.reward.fillorder_00.txt | 1 + .../snap.translate.reward.fillorder_01.txt | 1 + .../snap.translate.reward.fillorder_02.txt | 1 + ...p.translate.tlockonly.anyonecantake_00.txt | 1 + ...p.translate.tlockonly.anyonecantake_01.txt | 1 + ...nap.translate.tlockonly.cancelorder_00.txt | 1 + ...nap.translate.tlockonly.cancelorder_01.txt | 1 + ...nap.translate.tlockonly.cancelorder_02.txt | 1 + ...nap.translate.tlockonly.cancelorder_03.txt | 1 + .../snap.translate.tlockonly.fillorder_00.txt | 1 + .../snap.translate.tlockonly.fillorder_01.txt | 1 + .../snap.translate.tlockonly.fillorder_02.txt | 1 + .../snap.translate.txn.anyonecantake_00.txt | 1 + .../snap.translate.txn.anyonecantake_01.txt | 1 + .../snap.translate.txn.cancelorder_00.txt | 1 + .../snap.translate.txn.cancelorder_01.txt | 1 + .../snap.translate.txn.cancelorder_02.txt | 1 + .../snap.translate.txn.cancelorder_03.txt | 1 + .../snap.translate.txn.fillorder_00.txt | 1 + .../snap.translate.txn.fillorder_01.txt | 1 + .../snap.translate.txn.fillorder_02.txt | 1 + mintscript/src/translate.rs | 12 +++--- 30 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 mintscript/src/tests/translate/snap.translate.reward.anyonecantake_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.reward.anyonecantake_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.reward.cancelorder_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.reward.cancelorder_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.reward.cancelorder_02.txt create mode 100644 mintscript/src/tests/translate/snap.translate.reward.cancelorder_03.txt create mode 100644 mintscript/src/tests/translate/snap.translate.reward.fillorder_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.reward.fillorder_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.reward.fillorder_02.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.anyonecantake_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.anyonecantake_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_02.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_03.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_02.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.anyonecantake_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.anyonecantake_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.cancelorder_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.cancelorder_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.cancelorder_02.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.cancelorder_03.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.fillorder_00.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.fillorder_01.txt create mode 100644 mintscript/src/tests/translate/snap.translate.txn.fillorder_02.txt diff --git a/mintscript/src/tests/translate/mocks.rs b/mintscript/src/tests/translate/mocks.rs index 4979211a01..80be2f48b0 100644 --- a/mintscript/src/tests/translate/mocks.rs +++ b/mintscript/src/tests/translate/mocks.rs @@ -16,6 +16,7 @@ use super::super::*; use crate::translate::InputInfo; +use common::chain::{OrderData, OrderId}; use pos_accounting::{DelegationData, PoolData}; use tokens_accounting::TokenData; @@ -27,6 +28,7 @@ pub struct MockSigInfoProvider<'a> { tokens: BTreeMap, pools: BTreeMap, delegations: BTreeMap, + orders: BTreeMap, } impl<'a> MockSigInfoProvider<'a> { @@ -36,6 +38,7 @@ impl<'a> MockSigInfoProvider<'a> { tokens: impl IntoIterator, pools: impl IntoIterator, delegations: impl IntoIterator, + orders: impl IntoIterator, ) -> Self { Self { input_info, @@ -43,6 +46,7 @@ impl<'a> MockSigInfoProvider<'a> { tokens: tokens.into_iter().collect(), pools: pools.into_iter().collect(), delegations: delegations.into_iter().collect(), + orders: orders.into_iter().collect(), } } } @@ -129,18 +133,15 @@ impl tokens_accounting::TokensAccountingView for MockSigInfoProvider<'_> { impl orders_accounting::OrdersAccountingView for MockSigInfoProvider<'_> { type Error = orders_accounting::Error; - fn get_order_data( - &self, - id: &common::chain::OrderId, - ) -> Result, Self::Error> { - todo!() + fn get_order_data(&self, id: &OrderId) -> Result, Self::Error> { + Ok(self.orders.get(id).cloned()) } - fn get_ask_balance(&self, id: &common::chain::OrderId) -> Result, Self::Error> { - todo!() + fn get_ask_balance(&self, _id: &OrderId) -> Result, Self::Error> { + unreachable!() } - fn get_give_balance(&self, id: &common::chain::OrderId) -> Result, Self::Error> { - todo!() + fn get_give_balance(&self, _id: &OrderId) -> Result, Self::Error> { + unreachable!() } } diff --git a/mintscript/src/tests/translate/mod.rs b/mintscript/src/tests/translate/mod.rs index e80d5ac5c6..f618605ccd 100644 --- a/mintscript/src/tests/translate/mod.rs +++ b/mintscript/src/tests/translate/mod.rs @@ -22,7 +22,7 @@ use common::{ htlc::{HashedTimelockContract, HtlcSecret, HtlcSecretHash}, signature::inputsig::authorize_hashed_timelock_contract_spend::AuthorizedHashedTimelockContractSpend, stakelock::StakePoolData, - tokens, AccountNonce, AccountSpending, + tokens, AccountNonce, AccountSpending, OrderData, OrderId, }, primitives::per_thousand::PerThousand, }; @@ -210,6 +210,19 @@ fn pool0() -> (PoolId, PoolData) { (fake_id(0xbc), data) } +fn order0() -> (OrderId, OrderData) { + let data = OrderData::new( + dest_pk(0x33), + OutputValue::Coin(Amount::from_atoms(100)), + OutputValue::Coin(Amount::from_atoms(200)), + ); + (fake_id(0x44), data) +} + +fn anyonecantake(data: OrderData) -> TestInputInfo { + tii(TxOutput::AnyoneCanTake(data)) +} + fn account_spend(deleg: DelegationId, amount: u128) -> TestInputInfo { let spend = AccountSpending::DelegationBalance(deleg, Amount::from_atoms(amount)); let outpoint = AccountOutPoint::new(AccountNonce::new(7), spend); @@ -235,6 +248,17 @@ fn mint(id: TokenId, amount: u128) -> TestInputInfo { TestInputInfo::AccountCommand { command } } +fn cancel_order(id: OrderId) -> TestInputInfo { + let command = AccountCommand::CancelOrder(id); + TestInputInfo::AccountCommand { command } +} + +fn fill_order(id: OrderId) -> TestInputInfo { + let command = + AccountCommand::FillOrder(id, OutputValue::Coin(Amount::from_atoms(1)), dest_pk(0x4)); + TestInputInfo::AccountCommand { command } +} + // A hack to specify all the modes in the parametrized test below. The mode specification ought to // be simplified in the actual implementation and then this may be dropped. @@ -327,6 +351,15 @@ fn mode_name<'a, T: TranslationMode<'a>>(_: &T) -> &'static str { #[case("htlc_02", htlc(15, 16, tl_until_time(99)), htlc_stdsig(0x53))] #[case("htlc_03", htlc(17, 18, tl_for_secs(124)), htlc_multisig(0x54))] #[case("htlc_04", htlc(19, 20, tl_for_blocks(1000)), htlc_multisig(0x55))] +#[case("anyonecantake_00", anyonecantake(order0().1), nosig())] +#[case("anyonecantake_01", anyonecantake(order0().1), stdsig(0x57))] +#[case("cancelorder_00", cancel_order(order0().0), nosig())] +#[case("cancelorder_01", cancel_order(fake_id(0x88)), nosig())] +#[case("cancelorder_02", cancel_order(order0().0), stdsig(0x44))] +#[case("cancelorder_03", cancel_order(order0().0), stdsig(0x45))] +#[case("fillorder_00", fill_order(order0().0), nosig())] +#[case("fillorder_01", fill_order(fake_id(0x77)), nosig())] +#[case("fillorder_00", fill_order(order0().0), stdsig(0x45))] fn translate_snap( #[values(TxnMode, RewardMode, TimelockOnly)] mode: impl for<'a> TranslationMode<'a>, #[case] name: &str, @@ -337,7 +370,9 @@ fn translate_snap( let tokens = [token0()]; let delegs = [deleg0()]; let pools = [pool0()]; - let sig_info = mocks::MockSigInfoProvider::new(input_info, witness, tokens, pools, delegs); + let orders = [order0()]; + let sig_info = + mocks::MockSigInfoProvider::new(input_info, witness, tokens, pools, delegs, orders); let mode_str = mode_name(&mode); let result = match mode.translate_input_and_witness(&sig_info) { diff --git a/mintscript/src/tests/translate/snap.translate.reward.anyonecantake_00.txt b/mintscript/src/tests/translate/snap.translate.reward.anyonecantake_00.txt new file mode 100644 index 0000000000..776077671b --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.anyonecantake_00.txt @@ -0,0 +1 @@ +ERROR: Attempt to spend an unspendable output \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.reward.anyonecantake_01.txt b/mintscript/src/tests/translate/snap.translate.reward.anyonecantake_01.txt new file mode 100644 index 0000000000..776077671b --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.anyonecantake_01.txt @@ -0,0 +1 @@ +ERROR: Attempt to spend an unspendable output \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.reward.cancelorder_00.txt b/mintscript/src/tests/translate/snap.translate.reward.cancelorder_00.txt new file mode 100644 index 0000000000..d6ff8f8ae1 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.cancelorder_00.txt @@ -0,0 +1 @@ +ERROR: Illegal account spend \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.reward.cancelorder_01.txt b/mintscript/src/tests/translate/snap.translate.reward.cancelorder_01.txt new file mode 100644 index 0000000000..d6ff8f8ae1 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.cancelorder_01.txt @@ -0,0 +1 @@ +ERROR: Illegal account spend \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.reward.cancelorder_02.txt b/mintscript/src/tests/translate/snap.translate.reward.cancelorder_02.txt new file mode 100644 index 0000000000..d6ff8f8ae1 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.cancelorder_02.txt @@ -0,0 +1 @@ +ERROR: Illegal account spend \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.reward.cancelorder_03.txt b/mintscript/src/tests/translate/snap.translate.reward.cancelorder_03.txt new file mode 100644 index 0000000000..d6ff8f8ae1 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.cancelorder_03.txt @@ -0,0 +1 @@ +ERROR: Illegal account spend \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.reward.fillorder_00.txt b/mintscript/src/tests/translate/snap.translate.reward.fillorder_00.txt new file mode 100644 index 0000000000..d6ff8f8ae1 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.fillorder_00.txt @@ -0,0 +1 @@ +ERROR: Illegal account spend \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.reward.fillorder_01.txt b/mintscript/src/tests/translate/snap.translate.reward.fillorder_01.txt new file mode 100644 index 0000000000..d6ff8f8ae1 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.fillorder_01.txt @@ -0,0 +1 @@ +ERROR: Illegal account spend \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.reward.fillorder_02.txt b/mintscript/src/tests/translate/snap.translate.reward.fillorder_02.txt new file mode 100644 index 0000000000..d6ff8f8ae1 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.reward.fillorder_02.txt @@ -0,0 +1 @@ +ERROR: Illegal account spend \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.anyonecantake_00.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.anyonecantake_00.txt new file mode 100644 index 0000000000..776077671b --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.anyonecantake_00.txt @@ -0,0 +1 @@ +ERROR: Attempt to spend an unspendable output \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.anyonecantake_01.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.anyonecantake_01.txt new file mode 100644 index 0000000000..776077671b --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.anyonecantake_01.txt @@ -0,0 +1 @@ +ERROR: Attempt to spend an unspendable output \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_00.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_00.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_00.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_01.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_01.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_01.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_02.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_02.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_02.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_03.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_03.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_03.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_00.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_00.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_00.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_01.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_01.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_01.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_02.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_02.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.tlockonly.fillorder_02.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.txn.anyonecantake_00.txt b/mintscript/src/tests/translate/snap.translate.txn.anyonecantake_00.txt new file mode 100644 index 0000000000..776077671b --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.anyonecantake_00.txt @@ -0,0 +1 @@ +ERROR: Attempt to spend an unspendable output \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.txn.anyonecantake_01.txt b/mintscript/src/tests/translate/snap.translate.txn.anyonecantake_01.txt new file mode 100644 index 0000000000..776077671b --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.anyonecantake_01.txt @@ -0,0 +1 @@ +ERROR: Attempt to spend an unspendable output \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.txn.cancelorder_00.txt b/mintscript/src/tests/translate/snap.translate.txn.cancelorder_00.txt new file mode 100644 index 0000000000..ecd4b9ce80 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.cancelorder_00.txt @@ -0,0 +1 @@ +signature(0x02000236d8c927b785e27385737e82cdde2e06dc510ab8545d6eab0ca05c36040a437c, 0x0000) diff --git a/mintscript/src/tests/translate/snap.translate.txn.cancelorder_01.txt b/mintscript/src/tests/translate/snap.translate.txn.cancelorder_01.txt new file mode 100644 index 0000000000..44099c2e1b --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.cancelorder_01.txt @@ -0,0 +1 @@ +ERROR: Order with id 8888…8888 does not exist \ No newline at end of file diff --git a/mintscript/src/tests/translate/snap.translate.txn.cancelorder_02.txt b/mintscript/src/tests/translate/snap.translate.txn.cancelorder_02.txt new file mode 100644 index 0000000000..ef0b468b80 --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.cancelorder_02.txt @@ -0,0 +1 @@ +signature(0x02000236d8c927b785e27385737e82cdde2e06dc510ab8545d6eab0ca05c36040a437c, 0x0101084444) diff --git a/mintscript/src/tests/translate/snap.translate.txn.cancelorder_03.txt b/mintscript/src/tests/translate/snap.translate.txn.cancelorder_03.txt new file mode 100644 index 0000000000..976babc21a --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.cancelorder_03.txt @@ -0,0 +1 @@ +signature(0x02000236d8c927b785e27385737e82cdde2e06dc510ab8545d6eab0ca05c36040a437c, 0x0101084545) diff --git a/mintscript/src/tests/translate/snap.translate.txn.fillorder_00.txt b/mintscript/src/tests/translate/snap.translate.txn.fillorder_00.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.fillorder_00.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.txn.fillorder_01.txt b/mintscript/src/tests/translate/snap.translate.txn.fillorder_01.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.fillorder_01.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/tests/translate/snap.translate.txn.fillorder_02.txt b/mintscript/src/tests/translate/snap.translate.txn.fillorder_02.txt new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/mintscript/src/tests/translate/snap.translate.txn.fillorder_02.txt @@ -0,0 +1 @@ +true diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index 3480a17870..9bd419009d 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -238,13 +238,13 @@ impl TranslateInput for BlockRewardTransactable<'_> TxOutput::Transfer(_, _) | TxOutput::LockThenTransfer(_, _, _) | TxOutput::IssueNft(_, _, _) - | TxOutput::Htlc(_, _) - | TxOutput::AnyoneCanTake(_) => Err(TranslationError::IllegalOutputSpend), + | TxOutput::Htlc(_, _) => Err(TranslationError::IllegalOutputSpend), TxOutput::CreateDelegationId(_, _) | TxOutput::Burn(_) | TxOutput::DataDeposit(_) | TxOutput::DelegateStaking(_, _) - | TxOutput::IssueFungibleToken(_) => Err(TranslationError::Unspendable), + | TxOutput::IssueFungibleToken(_) + | TxOutput::AnyoneCanTake(_) => Err(TranslationError::Unspendable), TxOutput::ProduceBlockFromStake(d, _) => { // Spending an output of a block creation output is only allowed to @@ -296,13 +296,13 @@ impl TranslateInput for TimelockOnly { TxOutput::Transfer(_, _) | TxOutput::CreateStakePool(_, _) | TxOutput::ProduceBlockFromStake(_, _) - | TxOutput::IssueNft(_, _, _) - | TxOutput::AnyoneCanTake(_) => Ok(WitnessScript::TRUE), + | TxOutput::IssueNft(_, _, _) => Ok(WitnessScript::TRUE), TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::DelegateStaking(_, _) | TxOutput::Burn(_) - | TxOutput::DataDeposit(_) => Err(TranslationError::Unspendable), + | TxOutput::DataDeposit(_) + | TxOutput::AnyoneCanTake(_) => Err(TranslationError::Unspendable), }, InputInfo::Account { outpoint } => match outpoint.account() { AccountSpending::DelegationBalance(_deleg_id, _amt) => Ok(WitnessScript::TRUE), From fc25b9bad51bf9635310d24dbb350d0ade7a853c Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 23 Jun 2024 13:54:51 +0300 Subject: [PATCH 30/37] Add additional tests --- orders-accounting/src/price_calculation.rs | 36 ++++++++++++++++++++-- orders-accounting/src/tests/operations.rs | 34 ++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/orders-accounting/src/price_calculation.rs b/orders-accounting/src/price_calculation.rs index ad005a8a24..7a72602e99 100644 --- a/orders-accounting/src/price_calculation.rs +++ b/orders-accounting/src/price_calculation.rs @@ -117,6 +117,38 @@ mod tests { }; } + #[rstest] + #[case(0, 0, 0, None)] + #[case(0, 1, 1, None)] + #[case(0, u128::MAX, 1, None)] + #[case(3, u128::MAX, 2, None)] + #[case(1, 0, 0, Some(0))] + #[case(1, 0, 1, Some(0))] + #[case(1, 1, 1, Some(1))] + #[case(1, 2, 1, Some(2))] + #[case(2, 100, 0, Some(0))] + #[case(2, 100, 1, Some(50))] + #[case(2, 100, 2, Some(100))] + #[case(3, 100, 0, Some(0))] + #[case(3, 100, 1, Some(33))] + #[case(3, 100, 2, Some(66))] + #[case(3, 100, 3, Some(100))] + fn calculate_filled_amount_impl_test( + #[case] ask: u128, + #[case] give: u128, + #[case] fill: u128, + #[case] result: Option, + ) { + assert_eq!( + result.map(Amount::from_atoms), + calculate_filled_amount_impl( + Amount::from_atoms(ask), + Amount::from_atoms(give), + Amount::from_atoms(fill) + ) + ); + } + #[rstest] #[case(token!(1), coin!(0), token!(0), 0)] #[case(token!(1), coin!(0), token!(1), 0)] @@ -147,7 +179,7 @@ mod tests { #[case(coin!(3), coin!(100), coin!(2), 66)] #[case(coin!(3), coin!(100), coin!(3), 100)] #[case(coin!(1), token!(u128::MAX), coin!(1), u128::MAX)] - fn valid_values( + fn fill_order_valid_values( #[case] ask: OutputValue, #[case] give: OutputValue, #[case] fill: OutputValue, @@ -179,7 +211,7 @@ mod tests { #[case(coin!(1), token!(1), token!(1), Error::CurrencyMismatch)] #[case(coin!(1), token!(1), token!(1), Error::CurrencyMismatch)] #[case(token!(1), token2!(1), token2!(1), Error::CurrencyMismatch)] - fn invalid_values( + fn fill_order_invalid_values( #[case] ask: OutputValue, #[case] give: OutputValue, #[case] fill: OutputValue, diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index 4c2eaa7ee8..6cf0cabbb0 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -250,6 +250,40 @@ fn cancel_order_and_undo(#[case] seed: Seed) { assert_eq!(original_storage, storage); } +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_order_wrong_currency(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + + let order_id = OrderId::random_using(&mut rng); + let order_data = make_order_data(&mut rng); + + let mut storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data.clone())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + ); + let mut db = OrdersAccountingDB::new(&mut storage); + let mut cache = OrdersAccountingCache::new(&db); + + // try to fill with random token instead of a coin + { + let random_token_id = TokenId::random_using(&mut rng); + let result = cache.fill_order( + order_id, + OutputValue::TokenV1(random_token_id, Amount::from_atoms(1)), + ); + assert_eq!(result.unwrap_err(), Error::CurrencyMismatch); + } + + let _ = cache.fill_order(order_id, order_data.ask().clone()).unwrap(); + + db.batch_write_orders_data(cache.consume()).unwrap(); + + assert_eq!(InMemoryOrdersAccounting::new(), storage); +} + #[rstest] #[trace] #[case(Seed::from_entropy())] From ddbecb624604fbc7dc5faa747ab92f54bd71d21a Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 23 Jun 2024 14:05:07 +0300 Subject: [PATCH 31/37] Isolate output_value_amount function --- orders-accounting/src/cache.rs | 8 ++- orders-accounting/src/lib.rs | 9 ---- orders-accounting/src/price_calculation.rs | 24 ++++++--- orders-accounting/src/tests/operations.rs | 59 ++++++++++++---------- 4 files changed, 58 insertions(+), 42 deletions(-) diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index cc821d932d..054f7b2734 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -28,11 +28,17 @@ use crate::{ CancelOrderUndo, CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, OrdersAccountingUndo, }, - output_value_amount, view::OrdersAccountingView, FlushableOrdersAccountingView, OrdersAccountingDeltaUndoData, }; +fn output_value_amount(value: &OutputValue) -> Result { + match value { + OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => Ok(*amount), + OutputValue::TokenV0(_) => Err(Error::UnsupportedTokenVersion), + } +} + pub struct OrdersAccountingCache

{ parent: P, data: OrdersAccountingDeltaData, diff --git a/orders-accounting/src/lib.rs b/orders-accounting/src/lib.rs index 0edae1e4f9..7f0849e952 100644 --- a/orders-accounting/src/lib.rs +++ b/orders-accounting/src/lib.rs @@ -34,14 +34,5 @@ pub use { view::{FlushableOrdersAccountingView, OrdersAccountingView}, }; -use common::chain::output_value::OutputValue; - -fn output_value_amount(value: &OutputValue) -> crate::error::Result { - match value { - OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => Ok(*amount), - OutputValue::TokenV0(_) => Err(Error::UnsupportedTokenVersion), - } -} - #[cfg(test)] mod tests; diff --git a/orders-accounting/src/price_calculation.rs b/orders-accounting/src/price_calculation.rs index 7a72602e99..cbd13b3d15 100644 --- a/orders-accounting/src/price_calculation.rs +++ b/orders-accounting/src/price_calculation.rs @@ -19,7 +19,7 @@ use common::{ }; use utils::ensure; -use crate::{error::Result, output_value_amount, Error, OrdersAccountingView}; +use crate::{error::Result, Error, OrdersAccountingView}; pub fn calculate_fill_order( view: &impl OrdersAccountingView, @@ -49,7 +49,12 @@ pub fn calculate_fill_order( ensure_currencies_and_amounts_match(order_id, &ask_balance, fill_value)?; } - calculate_filled_amount_impl(ask_balance, give_balance, output_value_amount(fill_value)?) + let fill_amount = match fill_value { + OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => *amount, + OutputValue::TokenV0(_) => return Err(Error::UnsupportedTokenVersion), + }; + + calculate_filled_amount_impl(ask_balance, give_balance, fill_amount) .ok_or(Error::OrderOverflow(order_id)) } @@ -117,6 +122,13 @@ mod tests { }; } + fn output_value_amount(value: &OutputValue) -> Amount { + match value { + OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => *amount, + OutputValue::TokenV0(_) => panic!("unsupported token"), + } + } + #[rstest] #[case(0, 0, 0, None)] #[case(0, 1, 1, None)] @@ -191,8 +203,8 @@ mod tests { order_id, OrderData::new(Destination::AnyoneCanSpend, ask.clone(), give.clone()), )]), - BTreeMap::from_iter([(order_id, output_value_amount(&ask).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(&give).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(&ask))]), + BTreeMap::from_iter([(order_id, output_value_amount(&give))]), ); let orders_db = OrdersAccountingDB::new(&orders_store); @@ -223,8 +235,8 @@ mod tests { order_id, OrderData::new(Destination::AnyoneCanSpend, ask.clone(), give.clone()), )]), - BTreeMap::from_iter([(order_id, output_value_amount(&ask).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(&give).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(&ask))]), + BTreeMap::from_iter([(order_id, output_value_amount(&give))]), ); let orders_db = OrdersAccountingDB::new(&orders_store); diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index 6cf0cabbb0..7193c22383 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -24,7 +24,7 @@ use rstest::rstest; use test_utils::random::{make_seedable_rng, Seed}; use crate::{ - cache::OrdersAccountingCache, operations::OrdersAccountingOperations, output_value_amount, + cache::OrdersAccountingCache, operations::OrdersAccountingOperations, view::FlushableOrdersAccountingView, Error, InMemoryOrdersAccounting, OrdersAccountingDB, OrdersAccountingView, }; @@ -38,6 +38,13 @@ fn make_order_data(rng: &mut impl Rng) -> OrderData { ) } +fn output_value_amount(value: &OutputValue) -> Amount { + match value { + OutputValue::Coin(amount) | OutputValue::TokenV1(_, amount) => *amount, + OutputValue::TokenV0(_) => panic!("unsupported token"), + } +} + #[rstest] #[trace] #[case(Seed::from_entropy())] @@ -57,8 +64,8 @@ fn create_order_and_flush(#[case] seed: Seed) { let expected_storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); assert_eq!(expected_storage, storage); @@ -89,8 +96,8 @@ fn create_order_twice(#[case] seed: Seed) { { let storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let db = OrdersAccountingDB::new(&storage); let mut cache = OrdersAccountingCache::new(&db); @@ -122,11 +129,11 @@ fn create_order_and_undo(#[case] seed: Seed) { cache.get_order_data(&order_id).unwrap().as_ref() ); assert_eq!( - Some(output_value_amount(order_data.ask()).unwrap()), + Some(output_value_amount(order_data.ask())), cache.get_ask_balance(&order_id).unwrap() ); assert_eq!( - Some(output_value_amount(order_data.give()).unwrap()), + Some(output_value_amount(order_data.give())), cache.get_give_balance(&order_id).unwrap() ); @@ -152,8 +159,8 @@ fn cancel_order_and_flush(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); @@ -186,8 +193,8 @@ fn cancel_order_twice(#[case] seed: Seed) { let storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let db = OrdersAccountingDB::new(&storage); let mut cache = OrdersAccountingCache::new(&db); @@ -211,8 +218,8 @@ fn cancel_order_and_undo(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let original_storage = storage.clone(); let mut db = OrdersAccountingDB::new(&mut storage); @@ -237,11 +244,11 @@ fn cancel_order_and_undo(#[case] seed: Seed) { cache.get_order_data(&order_id).unwrap().as_ref() ); assert_eq!( - Some(output_value_amount(order_data.ask()).unwrap()), + Some(output_value_amount(order_data.ask())), cache.get_ask_balance(&order_id).unwrap() ); assert_eq!( - Some(output_value_amount(order_data.give()).unwrap()), + Some(output_value_amount(order_data.give())), cache.get_give_balance(&order_id).unwrap() ); @@ -261,8 +268,8 @@ fn fill_order_wrong_currency(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); @@ -295,8 +302,8 @@ fn fill_entire_order_and_flush(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); @@ -310,7 +317,7 @@ fn fill_entire_order_and_flush(#[case] seed: Seed) { // try to overbid { - let ask_amount = output_value_amount(order_data.ask()).unwrap(); + let ask_amount = output_value_amount(order_data.ask()); let fill = OutputValue::Coin((ask_amount + Amount::from_atoms(1)).unwrap()); let result = cache.fill_order(order_id, fill); assert_eq!( @@ -346,8 +353,8 @@ fn fill_order_partially_and_flush(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); @@ -420,8 +427,8 @@ fn fill_order_partially_and_undo(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let original_storage = storage.clone(); let mut db = OrdersAccountingDB::new(&mut storage); @@ -524,8 +531,8 @@ fn fill_order_partially_and_cancel(#[case] seed: Seed) { let mut storage = InMemoryOrdersAccounting::from_values( BTreeMap::from_iter([(order_id, order_data.clone())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()).unwrap())]), - BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()).unwrap())]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.ask()))]), + BTreeMap::from_iter([(order_id, output_value_amount(order_data.give()))]), ); let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); From 03da404ddb717f4faea3c28c8a23f07de7c92bb6 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Sun, 23 Jun 2024 15:57:50 +0300 Subject: [PATCH 32/37] Solve Some(0) problem --- Cargo.lock | 1 + chainstate/src/detail/ban_score.rs | 2 + chainstate/src/detail/error_classification.rs | 6 ++- .../test-framework/src/random_tx_maker.rs | 5 ++ mempool/src/error/ban_score.rs | 2 + orders-accounting/Cargo.toml | 1 + orders-accounting/src/cache.rs | 50 ++++++++++++++++--- orders-accounting/src/error.rs | 4 ++ orders-accounting/src/tests/operations.rs | 20 ++------ 9 files changed, 66 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0dc1dad924..7ea1670661 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4912,6 +4912,7 @@ dependencies = [ "chainstate-types", "common", "crypto", + "logging", "parity-scale-codec", "randomness", "rstest", diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index fbfc503e33..2afe51759c 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -682,6 +682,8 @@ impl BanScore for orders_accounting::Error { Error::AttemptedCancelNonexistingOrderData(_) => 100, Error::AttemptedCancelNonexistingAskBalance(_) => 100, Error::AttemptedCancelNonexistingGiveBalance(_) => 100, + Error::InvariantNonzeroAskBalanceForMissingOrder(_) => 100, + Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => 100, Error::UnsupportedTokenVersion => 100, Error::ViewFail => 0, Error::StorageWrite => 0, diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index 774813c3a2..b4d9c28378 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -918,7 +918,11 @@ impl BlockProcessingErrorClassification for orders_accounting::Error { | Error::AttemptedCancelNonexistingOrderData(_) | Error::AttemptedCancelNonexistingAskBalance(_) | Error::AttemptedCancelNonexistingGiveBalance(_) - | Error::UnsupportedTokenVersion => BlockProcessingErrorClass::BadBlock, + | Error::UnsupportedTokenVersion + | Error::InvariantNonzeroAskBalanceForMissingOrder(_) + | Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => { + BlockProcessingErrorClass::BadBlock + } Error::StorageError(err) => err.classify(), Error::AccountingError(err) => err.classify(), diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index ffd917ca12..16ecf9f2e5 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -667,7 +667,12 @@ impl<'a> RandomTxMaker<'a> { Amount::from_atoms(i128::MAX as u128) } }; + let supply_left = (supply_limit - circulating_supply).unwrap(); + if supply_left == Amount::ZERO { + return (Vec::new(), Vec::new()); + } + let mint_limit = std::cmp::min(100_000, supply_left.into_atoms()); let to_mint = Amount::from_atoms(rng.gen_range(1..=mint_limit)); diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index 99eb8eec8f..11b295eb1a 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -513,6 +513,8 @@ impl MempoolBanScore for orders_accounting::Error { Error::InvariantOrderDataExistForCancelUndo(_) => 100, Error::InvariantOrderAskBalanceExistForCancelUndo(_) => 100, Error::InvariantOrderGiveBalanceExistForCancelUndo(_) => 100, + Error::InvariantNonzeroAskBalanceForMissingOrder(_) => 100, + Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => 100, Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, diff --git a/orders-accounting/Cargo.toml b/orders-accounting/Cargo.toml index b4c45c76e5..d4e314e08c 100644 --- a/orders-accounting/Cargo.toml +++ b/orders-accounting/Cargo.toml @@ -12,6 +12,7 @@ accounting = { path = "../accounting" } chainstate-types = { path = "../chainstate/types" } common = { path = "../common" } crypto = { path = "../crypto" } +logging = { path = "../logging" } randomness = { path = "../randomness" } serialization = { path = "../serialization" } utils = { path = "../utils" } diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 054f7b2734..64314aa266 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -18,6 +18,7 @@ use common::{ chain::{output_value::OutputValue, OrderData, OrderId}, primitives::Amount, }; +use logging::log; use utils::ensure; use crate::{ @@ -61,12 +62,6 @@ impl OrdersAccountingCache

{ } fn undo_create_order(&mut self, undo: CreateOrderUndo) -> Result<()> { - ensure!( - self.get_order_data(&undo.id)?.is_some(), - Error::InvariantOrderDataNotFoundForUndo(undo.id) - ); - self.data.order_data.undo_merge_delta_data_element(undo.id, undo.undo_data)?; - match self.get_ask_balance(&undo.id)? { Some(balance) => { if balance != undo.ask_balance { @@ -87,6 +82,12 @@ impl OrdersAccountingCache

{ } self.data.give_balances.sub_unsigned(undo.id, undo.give_balance)?; + ensure!( + self.get_order_data(&undo.id)?.is_some(), + Error::InvariantOrderDataNotFoundForUndo(undo.id) + ); + self.data.order_data.undo_merge_delta_data_element(undo.id, undo.undo_data)?; + Ok(()) } @@ -144,18 +145,46 @@ impl OrdersAccountingView for OrdersAccountingCache

fn get_ask_balance(&self, id: &OrderId) -> Result> { let parent_supply = self.parent.get_ask_balance(id).map_err(|_| Error::ViewFail)?; let local_delta = self.data.ask_balances.data().get(id).cloned(); - combine_amount_delta(&parent_supply, &local_delta).map_err(Error::AccountingError) + let balance = + combine_amount_delta(&parent_supply, &local_delta).map_err(Error::AccountingError)?; + + // When combining deltas with amounts it's impossible to distinguish None from Some(Amount::ZERO). + // Use information from DeltaData to make the decision. + if self.get_order_data(id)?.is_some() { + Ok(balance) + } else { + utils::ensure!( + balance.unwrap_or(Amount::ZERO) == Amount::ZERO, + Error::InvariantNonzeroAskBalanceForMissingOrder(*id) + ); + Ok(None) + } } fn get_give_balance(&self, id: &OrderId) -> Result> { let parent_supply = self.parent.get_give_balance(id).map_err(|_| Error::ViewFail)?; let local_delta = self.data.give_balances.data().get(id).cloned(); - combine_amount_delta(&parent_supply, &local_delta).map_err(Error::AccountingError) + let balance = + combine_amount_delta(&parent_supply, &local_delta).map_err(Error::AccountingError)?; + + // When combining deltas with amounts it's impossible to distinguish None from Some(Amount::ZERO). + // Use information from DeltaData to make the decision. + if self.get_order_data(id)?.is_some() { + Ok(balance) + } else { + utils::ensure!( + balance.unwrap_or(Amount::ZERO) == Amount::ZERO, + Error::InvariantNonzeroGiveBalanceForMissingOrder(*id) + ); + Ok(None) + } } } impl OrdersAccountingOperations for OrdersAccountingCache

{ fn create_order(&mut self, id: OrderId, data: OrderData) -> Result { + log::debug!("Creating an order: {:?} {:?}", id, data); + ensure!( self.get_order_data(&id)?.is_none(), Error::OrderAlreadyExists(id) @@ -191,6 +220,8 @@ impl OrdersAccountingOperations for OrdersAccountingCac } fn cancel_order(&mut self, id: OrderId) -> Result { + log::debug!("Canceling an order: {:?}", id); + let order_data = self .get_order_data(&id)? .ok_or(Error::AttemptedCancelNonexistingOrderData(id))?; @@ -218,6 +249,8 @@ impl OrdersAccountingOperations for OrdersAccountingCac } fn fill_order(&mut self, id: OrderId, fill_value: OutputValue) -> Result { + log::debug!("Filling an order: {:?} {:?}", id, fill_value); + let fill_amount = output_value_amount(&fill_value)?; let filled_amount = calculate_fill_order(self, id, &fill_value)?; @@ -254,6 +287,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac } fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()> { + log::debug!("Uno an order: {:?}", undo_data); match undo_data { OrdersAccountingUndo::CreateOrder(undo) => self.undo_create_order(undo), OrdersAccountingUndo::CancelOrder(undo) => self.undo_cancel_order(undo), diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index 07049d8cba..c14ef175af 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -47,6 +47,10 @@ pub enum Error { InvariantOrderAskBalanceExistForCancelUndo(OrderId), #[error("Give balance for order {0}` still exist on cancel undo")] InvariantOrderGiveBalanceExistForCancelUndo(OrderId), + #[error("Ask balance for non-existing order {0}` is not zero")] + InvariantNonzeroAskBalanceForMissingOrder(OrderId), + #[error("Give balance for non-existing order {0}` is not zero")] + InvariantNonzeroGiveBalanceForMissingOrder(OrderId), #[error("Fill operation for order {0}` left a change")] FillOrderChangeLeft(OrderId), #[error("Coin type mismatch")] diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index 7193c22383..350d71f477 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -228,14 +228,8 @@ fn cancel_order_and_undo(#[case] seed: Seed) { let undo = cache.cancel_order(order_id).unwrap(); assert_eq!(None, cache.get_order_data(&order_id).unwrap().as_ref()); - assert_eq!( - Some(Amount::ZERO), - cache.get_ask_balance(&order_id).unwrap() - ); - assert_eq!( - Some(Amount::ZERO), - cache.get_give_balance(&order_id).unwrap() - ); + assert_eq!(None, cache.get_ask_balance(&order_id).unwrap()); + assert_eq!(None, cache.get_give_balance(&order_id).unwrap()); cache.undo(undo).unwrap(); @@ -456,14 +450,8 @@ fn fill_order_partially_and_undo(#[case] seed: Seed) { .unwrap(); assert_eq!(None, cache.get_order_data(&order_id).unwrap().as_ref()); - assert_eq!( - Some(Amount::ZERO), - cache.get_ask_balance(&order_id).unwrap() - ); - assert_eq!( - Some(Amount::ZERO), - cache.get_give_balance(&order_id).unwrap() - ); + assert_eq!(None, cache.get_ask_balance(&order_id).unwrap()); + assert_eq!(None, cache.get_give_balance(&order_id).unwrap()); cache.undo(undo3).unwrap(); From 72826045b98ea24cc520e26cb2dba4e7d95b0247 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Mon, 24 Jun 2024 17:19:07 +0300 Subject: [PATCH 33/37] Fix api simulation test --- Cargo.lock | 1 + .../scanner-lib/src/sync/tests/simulation.rs | 15 +++++++++------ orders-accounting/src/cache.rs | 2 +- tokens-accounting/Cargo.toml | 1 + tokens-accounting/src/cache.rs | 12 ++++++++++++ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ea1670661..a0c6a83eba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7558,6 +7558,7 @@ dependencies = [ "chainstate-types", "common", "crypto", + "logging", "parity-scale-codec", "randomness", "rstest", diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index d00744e6fb..61dbb115d9 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -406,7 +406,7 @@ async fn simulation( | TxOutput::LockThenTransfer(_, _, _) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::Htlc(_, _) => {} - TxOutput::AnyoneCanTake(_) => todo!(), + TxOutput::AnyoneCanTake(_) => unimplemented!(), // TODO: support orders }); tx.inputs().iter().for_each(|inp| match inp { @@ -427,7 +427,9 @@ async fn simulation( statistics .entry(CoinOrTokenStatistic::CirculatingSupply) .or_default() - .insert(CoinOrTokenId::TokenId(*token_id), *to_mint); + .entry(CoinOrTokenId::TokenId(*token_id)) + .and_modify(|amount| *amount = (*amount + *to_mint).unwrap()) + .or_insert(*to_mint); let token_supply_change_fee = chain_config.token_supply_change_fee(block_height); burn_coins(&mut statistics, token_supply_change_fee); @@ -451,8 +453,9 @@ async fn simulation( chain_config.token_change_authority_fee(block_height); burn_coins(&mut statistics, token_change_authority_fee); } - AccountCommand::CancelOrder(_) => todo!(), - AccountCommand::FillOrder(_, _, _) => todo!(), + AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { + unimplemented!() // TODO: support orders + } }, }); } @@ -471,6 +474,8 @@ async fn simulation( .and_modify(|amount| *amount = (*amount + block_subsidy).unwrap()) .or_insert(block_subsidy); + let pos_store = PoSAccountingAdapterToCheckFees::new(tf.chainstate.as_ref()); + let mut total_fees = AccumulatedFee::new(); for tx in block.transactions().iter() { let inputs_utxos: Vec<_> = tx @@ -484,8 +489,6 @@ async fn simulation( }) .collect(); - let pos_store = PoSAccountingAdapterToCheckFees::new(tf.chainstate.as_ref()); - let inputs_accumulator = ConstrainedValueAccumulator::from_inputs( &chain_config, block_height, diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 64314aa266..23d45d4305 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -287,7 +287,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac } fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()> { - log::debug!("Uno an order: {:?}", undo_data); + log::debug!("Undo an order: {:?}", undo_data); match undo_data { OrdersAccountingUndo::CreateOrder(undo) => self.undo_create_order(undo), OrdersAccountingUndo::CancelOrder(undo) => self.undo_cancel_order(undo), diff --git a/tokens-accounting/Cargo.toml b/tokens-accounting/Cargo.toml index 4b6d39e6ae..e55f989956 100644 --- a/tokens-accounting/Cargo.toml +++ b/tokens-accounting/Cargo.toml @@ -12,6 +12,7 @@ accounting = { path = "../accounting" } chainstate-types = { path = "../chainstate/types" } common = { path = "../common" } crypto = { path = "../crypto" } +logging = { path = "../logging" } randomness = { path = "../randomness" } serialization = { path = "../serialization" } utils = { path = "../utils" } diff --git a/tokens-accounting/src/cache.rs b/tokens-accounting/src/cache.rs index 057962acba..f0d32ac3f9 100644 --- a/tokens-accounting/src/cache.rs +++ b/tokens-accounting/src/cache.rs @@ -21,6 +21,7 @@ use common::{ }, primitives::Amount, }; +use logging::log; use crate::{ data::{TokenData, TokensAccountingDeltaData}, @@ -101,6 +102,8 @@ impl

FlushableTokensAccountingView for TokensAccountingCache

{ impl TokensAccountingOperations for TokensAccountingCache

{ fn issue_token(&mut self, id: TokenId, data: TokenData) -> Result { + log::debug!("Issuing a token: {:?} {:?}", id, data); + if self.get_token_data(&id)?.is_some() { return Err(Error::TokenAlreadyExists(id)); } @@ -125,6 +128,8 @@ impl TokensAccountingOperations for TokensAccountingCac id: TokenId, amount_to_add: Amount, ) -> Result { + log::debug!("Minting tokens: {:?} {:?}", id, amount_to_add); + let token_data = self.get_token_data(&id)?.ok_or(Error::TokenDataNotFound(id))?; let circulating_supply = self.get_circulating_supply(&id)?.unwrap_or(Amount::ZERO); @@ -165,6 +170,8 @@ impl TokensAccountingOperations for TokensAccountingCac id: TokenId, amount_to_burn: Amount, ) -> Result { + log::debug!("Unminting tokens: {:?} {:?}", id, amount_to_burn); + let token_data = self.get_token_data(&id)?.ok_or(Error::TokenDataNotFound(id))?; let circulating_supply = self.get_circulating_supply(&id)?.ok_or(Error::CirculatingSupplyNotFound(id))?; @@ -199,6 +206,7 @@ impl TokensAccountingOperations for TokensAccountingCac &mut self, id: TokenId, ) -> crate::error::Result { + log::debug!("Locking token supply: {:?}", id); let token_data = self.get_token_data(&id)?.ok_or(Error::TokenDataNotFound(id))?; let undo_data = match token_data { @@ -231,6 +239,7 @@ impl TokensAccountingOperations for TokensAccountingCac id: TokenId, is_unfreezable: IsTokenUnfreezable, ) -> Result { + log::debug!("Freezing token: {:?} {:?}", id, is_unfreezable); let token_data = self.get_token_data(&id)?.ok_or(Error::TokenDataNotFound(id))?; let undo_data = match token_data { @@ -258,6 +267,7 @@ impl TokensAccountingOperations for TokensAccountingCac } fn unfreeze_token(&mut self, id: TokenId) -> Result { + log::debug!("Unfreezing token supply: {:?}", id); let token_data = self.get_token_data(&id)?.ok_or(Error::TokenDataNotFound(id))?; let undo_data = match token_data { @@ -289,6 +299,7 @@ impl TokensAccountingOperations for TokensAccountingCac id: TokenId, new_authority: Destination, ) -> Result { + log::debug!("Changing token authority: {:?}", id); let token_data = self.get_token_data(&id)?.ok_or(Error::TokenDataNotFound(id))?; let undo_data = match token_data { @@ -313,6 +324,7 @@ impl TokensAccountingOperations for TokensAccountingCac } fn undo(&mut self, undo_data: TokenAccountingUndo) -> Result<(), Error> { + log::debug!("Undo in tokens: {:?}", undo_data); match undo_data { TokenAccountingUndo::IssueToken(undo) => { let _ = self From 8fe06847481feba5a82d9e4ef17457bd1f8a2e8d Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Mon, 24 Jun 2024 17:59:32 +0300 Subject: [PATCH 34/37] Fix clippy --- .../scanner-lib/src/blockchain_state/mod.rs | 6 +-- .../scanner-lib/src/sync/tests/simulation.rs | 6 +-- .../src/tests/orders_constraints.rs | 4 +- .../test-framework/src/random_tx_maker.rs | 6 +-- .../test-suite/src/tests/orders_tests.rs | 40 +++++++++---------- .../tests/outputs_utils.rs | 4 +- .../src/transaction_verifier/mod.rs | 2 +- common/src/chain/transaction/output/mod.rs | 2 +- mintscript/src/tests/translate/mod.rs | 2 +- wallet/src/account/currency_grouper/mod.rs | 4 +- wallet/src/account/mod.rs | 8 ++-- wallet/src/account/output_cache/mod.rs | 18 ++++----- 12 files changed, 51 insertions(+), 51 deletions(-) diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index e9f1e0c499..aac28720e3 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -737,7 +737,7 @@ async fn tx_fees( let pools = prefetch_pool_data(&inputs_utxos, db_tx).await?; let pos_accounting_adapter = PoSAccountingAdapterToCheckFees { pools }; - // TODO: support orders + // TODO(orders) let orders_store = orders_accounting::InMemoryOrdersAccounting::new(); let orders_db = orders_accounting::OrdersAccountingDB::new(&orders_store); @@ -1107,7 +1107,7 @@ async fn update_tables_from_transaction_inputs( .await; } AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { - // TODO: support orders + // TODO(orders) } }, TxInput::Account(outpoint) => { @@ -1674,7 +1674,7 @@ async fn update_tables_from_transaction_outputs( } TxOutput::Htlc(_, _) => {} // TODO(HTLC) TxOutput::AnyoneCanTake(_) => { - // TODO: support orders + // TODO(orders) } } } diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index 61dbb115d9..ca65b3a196 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -211,7 +211,7 @@ async fn simulation( let mut delegations = BTreeSet::new(); let mut token_ids = BTreeSet::new(); - // FIXME: proper impl + // TODO(orders) let orders_store = orders_accounting::InMemoryOrdersAccounting::new(); let orders_db = orders_accounting::OrdersAccountingDB::new(&orders_store); @@ -406,7 +406,7 @@ async fn simulation( | TxOutput::LockThenTransfer(_, _, _) | TxOutput::ProduceBlockFromStake(_, _) | TxOutput::Htlc(_, _) => {} - TxOutput::AnyoneCanTake(_) => unimplemented!(), // TODO: support orders + TxOutput::AnyoneCanTake(_) => unimplemented!(), // TODO(orders) }); tx.inputs().iter().for_each(|inp| match inp { @@ -454,7 +454,7 @@ async fn simulation( burn_coins(&mut statistics, token_change_authority_fee); } AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { - unimplemented!() // TODO: support orders + unimplemented!() // TODO(orders) } }, }); diff --git a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs index 802d7c3c98..4b7e733efc 100644 --- a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs +++ b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs @@ -46,11 +46,11 @@ fn create_order_constraints(#[case] seed: Seed) { let give_amount = Amount::from_atoms(rng.gen_range(100..1000)); let token_id = TokenId::random_using(&mut rng); let ask_amount = Amount::from_atoms(rng.gen_range(100..1000)); - let order_data = OrderData::new( + let order_data = Box::new(OrderData::new( Destination::AnyoneCanSpend, OutputValue::TokenV1(token_id, ask_amount), OutputValue::Coin(give_amount), - ); + )); let orders_store = InMemoryOrdersAccounting::new(); let orders_db = OrdersAccountingDB::new(&orders_store); diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index 16ecf9f2e5..9a8fd637f7 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -964,7 +964,7 @@ impl<'a> RandomTxMaker<'a> { // Transfer output is created intentionally besides order output to not waste utxo // (e.g. single genesis output on issuance) let outputs = vec![ - TxOutput::AnyoneCanTake(order_data), + TxOutput::AnyoneCanTake(Box::new(order_data)), TxOutput::Transfer( OutputValue::Coin(change), key_manager.new_destination(self.chainstate.get_chain_config(), rng), @@ -1287,7 +1287,7 @@ impl<'a> RandomTxMaker<'a> { let give_value = OutputValue::TokenV1(token_id, Amount::from_atoms(atoms)); let order_data = OrderData::new(Destination::AnyoneCanSpend, ask_value, give_value); - result_outputs.push(TxOutput::AnyoneCanTake(order_data)); + result_outputs.push(TxOutput::AnyoneCanTake(Box::new(order_data))); self.order_can_be_created = false; } else { // burn @@ -1377,7 +1377,7 @@ impl<'a> RandomTxMaker<'a> { } TxOutput::AnyoneCanTake(data) => { let order_id = make_order_id(inputs[0].utxo_outpoint().unwrap()); - let _ = orders_cache.create_order(order_id, data.clone()).unwrap(); + let _ = orders_cache.create_order(order_id, *data.clone()).unwrap(); Some(output) } }) diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index 929593513e..517cf3b4d0 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -118,7 +118,7 @@ fn create_order_check_storage(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data.clone()))) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -149,11 +149,11 @@ fn create_two_same_orders_in_tx(#[case] seed: Seed) { let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); - let order_data = OrderData::new( + let order_data = Box::new(OrderData::new( Destination::AnyoneCanSpend, OutputValue::Coin(ask_amount), OutputValue::TokenV1(token_id, give_amount), - ); + )); let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() @@ -203,8 +203,8 @@ fn create_two_orders_same_tx(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data_1)) - .add_output(TxOutput::AnyoneCanTake(order_data_2)) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data_1))) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data_2))) .build(); let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); @@ -233,11 +233,11 @@ fn create_two_orders_same_block(#[case] seed: Seed) { let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); - let order_data = OrderData::new( + let order_data = Box::new(OrderData::new( Destination::AnyoneCanSpend, OutputValue::Coin(ask_amount), OutputValue::TokenV1(token_id, give_amount), - ); + )); let tx1 = TransactionBuilder::new() .add_input( @@ -293,7 +293,7 @@ fn create_order_check_currencies(#[case] seed: Seed) { tokens_outpoint.clone().into(), InputWitness::NoSignature(None), ) - .add_output(TxOutput::AnyoneCanTake(order_data)) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data))) .build(); let tx_id = tx.transaction().get_id(); let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); @@ -326,7 +326,7 @@ fn create_order_check_currencies(#[case] seed: Seed) { tokens_outpoint.clone().into(), InputWitness::NoSignature(None), ) - .add_output(TxOutput::AnyoneCanTake(order_data)) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data))) .build(); let tx_id = tx.transaction().get_id(); let result = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); @@ -357,7 +357,7 @@ fn create_order_check_currencies(#[case] seed: Seed) { .add_transaction( TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data))) .build(), ) .build_and_process(&mut rng) @@ -392,7 +392,7 @@ fn create_order_tokens_for_tokens(#[case] seed: Seed) { .add_transaction( TransactionBuilder::new() .add_input(tokens_outpoint_2.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data))) .build(), ) .build_and_process(&mut rng) @@ -421,7 +421,7 @@ fn cancel_order_check_storage(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data))) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -474,7 +474,7 @@ fn fill_order_check_storage(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data.clone()))) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -584,7 +584,7 @@ fn fill_partially_then_cancel(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data))) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -673,7 +673,7 @@ fn cancel_order_check_signature(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data))) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -822,7 +822,7 @@ fn reorg_before_create(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data.clone()))) .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); @@ -910,7 +910,7 @@ fn reorg_after_create(#[case] seed: Seed) { let order_id = make_order_id(&tokens_outpoint); let tx = TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data.clone()))) // transfer output just to be able to spend something in alternative branch .add_output(TxOutput::Transfer( OutputValue::TokenV1(token_id, Amount::from_atoms(100)), @@ -1045,11 +1045,11 @@ fn test_activation(#[case] seed: Seed) { let (token_id, tokens_outpoint, _) = issue_and_mint_token_from_genesis(&mut rng, &mut tf); - let order_data = OrderData::new( + let order_data = Box::new(OrderData::new( Destination::AnyoneCanSpend, OutputValue::Coin(Amount::from_atoms(rng.gen_range(1u128..1000))), OutputValue::TokenV1(token_id, Amount::from_atoms(rng.gen_range(1u128..1000))), - ); + )); // Try to produce order output before activation, check an error let tx = TransactionBuilder::new() @@ -1083,7 +1083,7 @@ fn test_activation(#[case] seed: Seed) { .add_transaction( TransactionBuilder::new() .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) - .add_output(TxOutput::AnyoneCanTake(order_data.clone())) + .add_output(TxOutput::AnyoneCanTake(order_data)) .build(), ) .build_and_process(&mut rng) diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs index ad284eae0c..4544e4b374 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs @@ -238,11 +238,11 @@ pub fn data_deposit() -> TxOutput { } pub fn create_order() -> TxOutput { - TxOutput::AnyoneCanTake(OrderData::new( + TxOutput::AnyoneCanTake(Box::new(OrderData::new( Destination::AnyoneCanSpend, OutputValue::Coin(Amount::ZERO), OutputValue::Coin(Amount::ZERO), - )) + ))) } pub fn issue_nft() -> TxOutput { diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 61ecbfeba1..2a50f557c1 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -796,7 +796,7 @@ where let order_id = make_order_id(input_utxo_outpoint); let result = self .orders_accounting_cache - .create_order(order_id, order_data.clone()) + .create_order(order_id, *order_data.clone()) .map_err(ConnectTransactionError::OrdersAccountingError); Some(result) } diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs index 68fad37c99..1d4e0a7c1e 100644 --- a/common/src/chain/transaction/output/mod.rs +++ b/common/src/chain/transaction/output/mod.rs @@ -142,7 +142,7 @@ pub enum TxOutput { #[codec(index = 10)] Htlc(OutputValue, Box), #[codec(index = 11)] - AnyoneCanTake(OrderData), + AnyoneCanTake(Box), } impl TxOutput { diff --git a/mintscript/src/tests/translate/mod.rs b/mintscript/src/tests/translate/mod.rs index f618605ccd..b6bf4ecc62 100644 --- a/mintscript/src/tests/translate/mod.rs +++ b/mintscript/src/tests/translate/mod.rs @@ -220,7 +220,7 @@ fn order0() -> (OrderId, OrderData) { } fn anyonecantake(data: OrderData) -> TestInputInfo { - tii(TxOutput::AnyoneCanTake(data)) + tii(TxOutput::AnyoneCanTake(Box::new(data))) } fn account_spend(deleg: DelegationId, amount: u128) -> TestInputInfo { diff --git a/wallet/src/account/currency_grouper/mod.rs b/wallet/src/account/currency_grouper/mod.rs index be7ff06af9..4c3208c356 100644 --- a/wallet/src/account/currency_grouper/mod.rs +++ b/wallet/src/account/currency_grouper/mod.rs @@ -58,7 +58,7 @@ pub(crate) fn group_outputs( get_tx_output(&output).clone(), ))) } - // TODO: support orders + // TODO(orders) TxOutput::AnyoneCanTake(_) => unimplemented!(), }; @@ -114,7 +114,7 @@ pub fn group_outputs_with_issuance_fee( get_tx_output(&output).clone(), ))) } - // TODO: support orders + // TODO(orders) TxOutput::AnyoneCanTake(_) => unimplemented!(), }; diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index f9254395d1..920aa9cc17 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1347,7 +1347,7 @@ impl Account { .token_data(token_id) .map(|data| (None, Some(data.authority.clone()))) .ok_or(WalletError::UnknownTokenId(*token_id)), - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), } @@ -1828,7 +1828,7 @@ impl Account { self.find_token(token_id).is_ok() || self.is_destination_mine_or_watched(address) } - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, @@ -2194,7 +2194,7 @@ fn group_preselected_inputs( output.clone(), ))) } - // TODO: support orders + // TODO(orders) TxOutput::AnyoneCanTake(_) => unimplemented!(), }; update_preselected_inputs(currency, value, *fee)?; @@ -2239,7 +2239,7 @@ fn group_preselected_inputs( .ok_or(WalletError::OutputAmountOverflow)?, )?; } - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index 886394e390..4f2193472b 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -721,7 +721,7 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::ProduceBlockFromStake(_, _) => false, - // TODO: support orders + // TODO(orders) TxOutput::AnyoneCanTake(_) => unimplemented!(), } }), @@ -732,7 +732,7 @@ impl OutputCache { | AccountCommand::UnfreezeToken(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) | AccountCommand::UnmintTokens(token_id) => frozen_token_id == token_id, - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, @@ -837,7 +837,7 @@ impl OutputCache { } } TxOutput::IssueNft(_, _, _) => {} - // TODO: support orders + // TODO(orders) TxOutput::AnyoneCanTake(_) => unimplemented!(), }; } @@ -927,7 +927,7 @@ impl OutputCache { self.token_issuance.insert(*token_id, data); } } - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, @@ -1030,7 +1030,7 @@ impl OutputCache { data.unconfirmed_txs.remove(tx_id); } } - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, @@ -1058,7 +1058,7 @@ impl OutputCache { | TxOutput::CreateDelegationId(_, _) | TxOutput::IssueFungibleToken(_) | TxOutput::Htlc(_, _) => {} - // TODO: support orders + // TODO(orders) TxOutput::AnyoneCanTake(_) => unimplemented!(), } } @@ -1323,7 +1323,7 @@ impl OutputCache { data.unconfirmed_txs.remove(&tx_id.into()); } } - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, @@ -1532,7 +1532,7 @@ fn apply_freeze_mutations_from_tx( | AccountCommand::UnmintTokens(_) | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, @@ -1574,7 +1574,7 @@ fn apply_total_supply_mutations_from_tx( AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} - // TODO: support orders + // TODO(orders) AccountCommand::CancelOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, From e5c4702d234f693fb276740938e22134569de952 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Wed, 26 Jun 2024 16:00:00 +0300 Subject: [PATCH 35/37] Rename cancel order to conclude --- .../scanner-lib/src/blockchain_state/mod.rs | 4 +-- .../scanner-lib/src/sync/tests/simulation.rs | 2 +- api-server/web-server/src/api/json_helpers.rs | 6 ++--- .../src/constraints_accumulator.rs | 2 +- .../src/tests/orders_constraints.rs | 10 +++---- chainstate/src/detail/ban_score.rs | 12 ++++----- chainstate/src/detail/error_classification.rs | 12 ++++----- chainstate/src/rpc/types/account.rs | 4 +-- chainstate/src/rpc/types/output.rs | 2 +- .../test-framework/src/random_tx_maker.rs | 6 ++--- .../src/signature_destination_getter.rs | 4 +-- .../test-suite/src/tests/orders_tests.rs | 26 +++++++++---------- .../tests/outputs_utils.rs | 2 +- .../src/transaction_verifier/mod.rs | 6 ++--- common/src/chain/order.rs | 12 ++++----- common/src/chain/tokens/tokens_utils.rs | 2 +- .../src/chain/transaction/account_outpoint.rs | 4 +-- common/src/chain/transaction/output/mod.rs | 4 +-- mempool/src/error/ban_score.rs | 12 ++++----- mempool/src/pool/entry.rs | 2 +- mintscript/src/tests/translate/mod.rs | 12 ++++----- ...nap.translate.reward.concludeorder_00.txt} | 0 ...nap.translate.reward.concludeorder_01.txt} | 0 ...nap.translate.reward.concludeorder_02.txt} | 0 ...nap.translate.reward.concludeorder_03.txt} | 0 ....translate.tlockonly.concludeorder_00.txt} | 0 ....translate.tlockonly.concludeorder_01.txt} | 0 ....translate.tlockonly.concludeorder_02.txt} | 0 ....translate.tlockonly.concludeorder_03.txt} | 0 ...> snap.translate.txn.concludeorder_00.txt} | 0 ...> snap.translate.txn.concludeorder_01.txt} | 0 ...> snap.translate.txn.concludeorder_02.txt} | 0 ...> snap.translate.txn.concludeorder_03.txt} | 0 mintscript/src/translate.rs | 6 ++--- orders-accounting/src/cache.rs | 26 +++++++++---------- orders-accounting/src/error.rs | 24 ++++++++--------- orders-accounting/src/operations.rs | 6 ++--- orders-accounting/src/tests/operations.rs | 26 +++++++++---------- wallet/src/account/mod.rs | 6 ++--- wallet/src/account/output_cache/mod.rs | 14 +++++----- 40 files changed, 127 insertions(+), 127 deletions(-) rename mintscript/src/tests/translate/{snap.translate.reward.cancelorder_00.txt => snap.translate.reward.concludeorder_00.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.reward.cancelorder_01.txt => snap.translate.reward.concludeorder_01.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.reward.cancelorder_02.txt => snap.translate.reward.concludeorder_02.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.reward.cancelorder_03.txt => snap.translate.reward.concludeorder_03.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.tlockonly.cancelorder_00.txt => snap.translate.tlockonly.concludeorder_00.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.tlockonly.cancelorder_01.txt => snap.translate.tlockonly.concludeorder_01.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.tlockonly.cancelorder_02.txt => snap.translate.tlockonly.concludeorder_02.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.tlockonly.cancelorder_03.txt => snap.translate.tlockonly.concludeorder_03.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.txn.cancelorder_00.txt => snap.translate.txn.concludeorder_00.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.txn.cancelorder_01.txt => snap.translate.txn.concludeorder_01.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.txn.cancelorder_02.txt => snap.translate.txn.concludeorder_02.txt} (100%) rename mintscript/src/tests/translate/{snap.translate.txn.cancelorder_03.txt => snap.translate.txn.concludeorder_03.txt} (100%) diff --git a/api-server/scanner-lib/src/blockchain_state/mod.rs b/api-server/scanner-lib/src/blockchain_state/mod.rs index aac28720e3..522ed80da8 100644 --- a/api-server/scanner-lib/src/blockchain_state/mod.rs +++ b/api-server/scanner-lib/src/blockchain_state/mod.rs @@ -617,7 +617,7 @@ async fn calculate_fees( | AccountCommand::UnfreezeToken(token_id) | AccountCommand::LockTokenSupply(token_id) | AccountCommand::ChangeTokenAuthority(token_id, _) => Some(*token_id), - AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => None, + AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => None, }, }) .collect(); @@ -1106,7 +1106,7 @@ async fn update_tables_from_transaction_inputs( ) .await; } - AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { + AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => { // TODO(orders) } }, diff --git a/api-server/scanner-lib/src/sync/tests/simulation.rs b/api-server/scanner-lib/src/sync/tests/simulation.rs index ca65b3a196..16e18d4897 100644 --- a/api-server/scanner-lib/src/sync/tests/simulation.rs +++ b/api-server/scanner-lib/src/sync/tests/simulation.rs @@ -453,7 +453,7 @@ async fn simulation( chain_config.token_change_authority_fee(block_height); burn_coins(&mut statistics, token_change_authority_fee); } - AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { + AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => { unimplemented!() // TODO(orders) } }, diff --git a/api-server/web-server/src/api/json_helpers.rs b/api-server/web-server/src/api/json_helpers.rs index 3bf4b7adbe..73a88d971e 100644 --- a/api-server/web-server/src/api/json_helpers.rs +++ b/api-server/web-server/src/api/json_helpers.rs @@ -200,7 +200,7 @@ pub fn txoutput_to_json( TxOutput::AnyoneCanTake(data) => { json!({ "type": "AnyoneCanTake", - "cancel_key": Address::new(chain_config, data.cancel_key().clone()).expect("no error").as_str(), + "conclude_key": Address::new(chain_config, data.conclude_key().clone()).expect("no error").as_str(), "ask_value": outputvalue_to_json(data.ask(), chain_config, token_decimals), "give_value": outputvalue_to_json(data.give(), chain_config, token_decimals), }) @@ -345,10 +345,10 @@ pub fn tx_input_to_json( "nonce": nonce, }) } - AccountCommand::CancelOrder(order_id) => { + AccountCommand::ConcludeOrder(order_id) => { json!({ "input_type": "AccountCommand", - "command": "CancelOrder", + "command": "ConcludeOrder", "order_id": Address::new(chain_config, *order_id).expect("addressable").to_string(), }) } diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index 6f7e429e47..aac489a45f 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -266,7 +266,7 @@ impl ConstrainedValueAccumulator { CoinOrTokenId::Coin, chain_config.token_change_authority_fee(block_height), )), - AccountCommand::CancelOrder(id) => { + AccountCommand::ConcludeOrder(id) => { let order_data = orders_accounting_view .get_order_data(id) .map_err(|_| orders_accounting::Error::ViewFail)? diff --git a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs index 4b7e733efc..87162b0c63 100644 --- a/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs +++ b/chainstate/constraints-value-accumulator/src/tests/orders_constraints.rs @@ -567,7 +567,7 @@ fn fill_order_constraints(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn cancel_order_constraints(#[case] seed: Seed) { +fn conclude_order_constraints(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let chain_config = create_unit_test_config(); @@ -597,7 +597,7 @@ fn cancel_order_constraints(#[case] seed: Seed) { { let inputs = vec![TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), )]; let input_utxos = vec![None]; @@ -632,7 +632,7 @@ fn cancel_order_constraints(#[case] seed: Seed) { { let inputs = vec![TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), )]; let input_utxos = vec![None]; @@ -669,7 +669,7 @@ fn cancel_order_constraints(#[case] seed: Seed) { // partially use input in command let inputs = vec![TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), )]; let input_utxos = vec![None]; @@ -704,7 +704,7 @@ fn cancel_order_constraints(#[case] seed: Seed) { // valid case let inputs = vec![TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), )]; let input_utxos = vec![None]; diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 2afe51759c..24e67f3e04 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -672,16 +672,16 @@ impl BanScore for orders_accounting::Error { Error::InvariantOrderAskBalanceChangedForUndo(_) => 100, Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => 100, Error::InvariantOrderGiveBalanceChangedForUndo(_) => 100, - Error::InvariantOrderDataExistForCancelUndo(_) => 100, - Error::InvariantOrderAskBalanceExistForCancelUndo(_) => 100, - Error::InvariantOrderGiveBalanceExistForCancelUndo(_) => 100, + Error::InvariantOrderDataExistForConcludeUndo(_) => 100, + Error::InvariantOrderAskBalanceExistForConcludeUndo(_) => 100, + Error::InvariantOrderGiveBalanceExistForConcludeUndo(_) => 100, Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, Error::OrderOverbid(_, _, _) => 100, - Error::AttemptedCancelNonexistingOrderData(_) => 100, - Error::AttemptedCancelNonexistingAskBalance(_) => 100, - Error::AttemptedCancelNonexistingGiveBalance(_) => 100, + Error::AttemptedConcludeNonexistingOrderData(_) => 100, + Error::AttemptedConcludeNonexistingAskBalance(_) => 100, + Error::AttemptedConcludeNonexistingGiveBalance(_) => 100, Error::InvariantNonzeroAskBalanceForMissingOrder(_) => 100, Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => 100, Error::UnsupportedTokenVersion => 100, diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index b4d9c28378..65608a6ea1 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -908,16 +908,16 @@ impl BlockProcessingErrorClassification for orders_accounting::Error { | Error::InvariantOrderAskBalanceChangedForUndo(_) | Error::InvariantOrderGiveBalanceNotFoundForUndo(_) | Error::InvariantOrderGiveBalanceChangedForUndo(_) - | Error::InvariantOrderDataExistForCancelUndo(_) - | Error::InvariantOrderAskBalanceExistForCancelUndo(_) - | Error::InvariantOrderGiveBalanceExistForCancelUndo(_) + | Error::InvariantOrderDataExistForConcludeUndo(_) + | Error::InvariantOrderAskBalanceExistForConcludeUndo(_) + | Error::InvariantOrderGiveBalanceExistForConcludeUndo(_) | Error::FillOrderChangeLeft(_) | Error::CurrencyMismatch | Error::OrderOverflow(_) | Error::OrderOverbid(_, _, _) - | Error::AttemptedCancelNonexistingOrderData(_) - | Error::AttemptedCancelNonexistingAskBalance(_) - | Error::AttemptedCancelNonexistingGiveBalance(_) + | Error::AttemptedConcludeNonexistingOrderData(_) + | Error::AttemptedConcludeNonexistingAskBalance(_) + | Error::AttemptedConcludeNonexistingGiveBalance(_) | Error::UnsupportedTokenVersion | Error::InvariantNonzeroAskBalanceForMissingOrder(_) | Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => { diff --git a/chainstate/src/rpc/types/account.rs b/chainstate/src/rpc/types/account.rs index e11e2d1168..021db073bc 100644 --- a/chainstate/src/rpc/types/account.rs +++ b/chainstate/src/rpc/types/account.rs @@ -74,7 +74,7 @@ pub enum RpcAccountCommand { token_id: RpcAddress, new_authority: RpcAddress, }, - CancelOrder { + ConcludeOrder { order_id: RpcAddress, }, FillOrder { @@ -113,7 +113,7 @@ impl RpcAccountCommand { new_authority: RpcAddress::new(chain_config, destination.clone())?, } } - AccountCommand::CancelOrder(id) => RpcAccountCommand::CancelOrder { + AccountCommand::ConcludeOrder(id) => RpcAccountCommand::ConcludeOrder { order_id: RpcAddress::new(chain_config, *id)?, }, AccountCommand::FillOrder(id, fill, dest) => RpcAccountCommand::FillOrder { diff --git a/chainstate/src/rpc/types/output.rs b/chainstate/src/rpc/types/output.rs index 3622cac5b7..2084087b2f 100644 --- a/chainstate/src/rpc/types/output.rs +++ b/chainstate/src/rpc/types/output.rs @@ -209,7 +209,7 @@ impl RpcTxOutput { data: RpcHexString::from_bytes(data), }, TxOutput::AnyoneCanTake(data) => RpcTxOutput::AnyoneCanTake { - authority: RpcAddress::new(chain_config, data.cancel_key().clone())?, + authority: RpcAddress::new(chain_config, data.conclude_key().clone())?, ask_value: RpcOutputValue::new(chain_config, data.ask().clone())?, give_value: RpcOutputValue::new(chain_config, data.give().clone())?, }, diff --git a/chainstate/test-framework/src/random_tx_maker.rs b/chainstate/test-framework/src/random_tx_maker.rs index 9a8fd637f7..6825f0abbb 100644 --- a/chainstate/test-framework/src/random_tx_maker.rs +++ b/chainstate/test-framework/src/random_tx_maker.rs @@ -516,7 +516,7 @@ impl<'a> RandomTxMaker<'a> { } AccountType::Order(order_id) => { if !self.account_command_used { - // cancel an order + // conclude an order let order_data = orders_cache.get_order_data(&order_id).unwrap().unwrap(); if token_not_frozen(order_data.ask(), tokens_cache) && token_not_frozen(order_data.give(), tokens_cache) @@ -524,7 +524,7 @@ impl<'a> RandomTxMaker<'a> { let new_nonce = self.get_next_nonce(AccountType::Order(order_id)); result_inputs.push(TxInput::AccountCommand( new_nonce, - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), )); let available_give_balance = @@ -540,7 +540,7 @@ impl<'a> RandomTxMaker<'a> { let filled_output = output_value_with_amount(order_data.ask(), filled_amount); - let _ = orders_cache.cancel_order(order_id).unwrap(); + let _ = orders_cache.conclude_order(order_id).unwrap(); self.account_command_used = true; result_outputs.extend(vec![ diff --git a/chainstate/test-framework/src/signature_destination_getter.rs b/chainstate/test-framework/src/signature_destination_getter.rs index 69ea74d150..4b244b16fa 100644 --- a/chainstate/test-framework/src/signature_destination_getter.rs +++ b/chainstate/test-framework/src/signature_destination_getter.rs @@ -168,7 +168,7 @@ impl<'a> SignatureDestinationGetter<'a> { }; Ok(destination) } - AccountCommand::CancelOrder(order_id) => { + AccountCommand::ConcludeOrder(order_id) => { let order_data = orders_view .get_order_data(order_id) .map_err(|_| { @@ -179,7 +179,7 @@ impl<'a> SignatureDestinationGetter<'a> { .ok_or(SignatureDestinationGetterError::OrderDataNotFound( *order_id, ))?; - Ok(order_data.cancel_key().clone()) + Ok(order_data.conclude_key().clone()) } AccountCommand::FillOrder(_, _, d) => Ok(d.clone()), }, diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index 517cf3b4d0..1555414a09 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -403,7 +403,7 @@ fn create_order_tokens_for_tokens(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn cancel_order_check_storage(#[case] seed: Seed) { +fn conclude_order_check_storage(#[case] seed: Seed) { utils::concurrency::model(move || { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); @@ -429,7 +429,7 @@ fn cancel_order_check_storage(#[case] seed: Seed) { .add_input( TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -565,7 +565,7 @@ fn fill_order_check_storage(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn fill_partially_then_cancel(#[case] seed: Seed) { +fn fill_partially_then_conclude(#[case] seed: Seed) { utils::concurrency::model(move || { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); @@ -618,12 +618,12 @@ fn fill_partially_then_cancel(#[case] seed: Seed) { .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); - // cancel the order + // conclude the order let tx = TransactionBuilder::new() .add_input( TxInput::AccountCommand( AccountNonce::new(1), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -653,7 +653,7 @@ fn fill_partially_then_cancel(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn cancel_order_check_signature(#[case] seed: Seed) { +fn conclude_order_check_signature(#[case] seed: Seed) { utils::concurrency::model(move || { let mut rng = make_seedable_rng(seed); let mut tf = TestFramework::builder(&mut rng).build(); @@ -677,13 +677,13 @@ fn cancel_order_check_signature(#[case] seed: Seed) { .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); - // try cancel without signature + // try conclude without signature { let tx = TransactionBuilder::new() .add_input( TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -707,13 +707,13 @@ fn cancel_order_check_signature(#[case] seed: Seed) { ) } - // try cancel with wrong signature + // try conclude with wrong signature { let tx = TransactionBuilder::new() .add_input( TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -765,7 +765,7 @@ fn cancel_order_check_signature(#[case] seed: Seed) { .add_input( TxInput::AccountCommand( AccountNonce::new(0), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), ), InputWitness::NoSignature(None), ) @@ -886,7 +886,7 @@ fn reorg_before_create(#[case] seed: Seed) { }); } -// Create a chain with an order which is filled partially and then canceled. +// Create a chain with an order which is filled partially and then concluded. // Reorg from a point after the order was created, so that after reorg storage has original information on the order #[rstest] #[trace] @@ -965,7 +965,7 @@ fn reorg_after_create(#[case] seed: Seed) { .add_input( TxInput::AccountCommand( AccountNonce::new(1), - AccountCommand::CancelOrder(order_id), + AccountCommand::ConcludeOrder(order_id), ), InputWitness::NoSignature(None), ) diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs index 4544e4b374..18916f2986 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy/tests/outputs_utils.rs @@ -154,7 +154,7 @@ pub fn all_account_inputs() -> [TxInput; 9] { ), TxInput::from_command( AccountNonce::new(0), - AccountCommand::CancelOrder(OrderId::zero()), + AccountCommand::ConcludeOrder(OrderId::zero()), ), TxInput::from_command( AccountNonce::new(0), diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs index 2a50f557c1..596ecac756 100644 --- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs +++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs @@ -610,7 +610,7 @@ where }); Some(res) } - AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => None, + AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => None, }, }) .collect::, _>>()?; @@ -751,12 +751,12 @@ where | AccountCommand::FreezeToken(..) | AccountCommand::UnfreezeToken(..) | AccountCommand::ChangeTokenAuthority(..) => None, - AccountCommand::CancelOrder(order_id) => { + AccountCommand::ConcludeOrder(order_id) => { let res = self .spend_input_from_account(*nonce, account_op.clone().into()) .and_then(|_| { self.orders_accounting_cache - .cancel_order(*order_id) + .conclude_order(*order_id) .map_err(ConnectTransactionError::OrdersAccountingError) }); Some(res) diff --git a/common/src/chain/order.rs b/common/src/chain/order.rs index ad7808d95b..2fd22c536c 100644 --- a/common/src/chain/order.rs +++ b/common/src/chain/order.rs @@ -83,8 +83,8 @@ pub fn make_order_id(input0_outpoint: &UtxoOutPoint) -> OrderId { /// The fields represent currencies and amounts to be exchanged and the trading pair can be deducted from it. #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] pub struct OrderData { - /// The key that can authorize cancellation of an order - cancel_key: Destination, + /// The key that can authorize conclusion of an order + conclude_key: Destination, /// `Ask` and `give` fields represent amounts of currencies /// that an order maker wants to exchange, e.g. 5 coins for 10 tokens ask: OutputValue, @@ -92,16 +92,16 @@ pub struct OrderData { } impl OrderData { - pub fn new(cancel_key: Destination, ask: OutputValue, give: OutputValue) -> Self { + pub fn new(conclude_key: Destination, ask: OutputValue, give: OutputValue) -> Self { Self { - cancel_key, + conclude_key, ask, give, } } - pub fn cancel_key(&self) -> &Destination { - &self.cancel_key + pub fn conclude_key(&self) -> &Destination { + &self.conclude_key } pub fn ask(&self) -> &OutputValue { diff --git a/common/src/chain/tokens/tokens_utils.rs b/common/src/chain/tokens/tokens_utils.rs index 06427cf4a6..47ac24a832 100644 --- a/common/src/chain/tokens/tokens_utils.rs +++ b/common/src/chain/tokens/tokens_utils.rs @@ -56,7 +56,7 @@ pub fn get_token_supply_change_count(inputs: &[TxInput]) -> usize { AccountCommand::FreezeToken(_, _) | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) - | AccountCommand::CancelOrder(_) + | AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => false, AccountCommand::MintTokens(_, _) | AccountCommand::UnmintTokens(_) diff --git a/common/src/chain/transaction/account_outpoint.rs b/common/src/chain/transaction/account_outpoint.rs index fc4b3d901e..9ad087363a 100644 --- a/common/src/chain/transaction/account_outpoint.rs +++ b/common/src/chain/transaction/account_outpoint.rs @@ -53,7 +53,7 @@ impl From for AccountType { | AccountCommand::FreezeToken(id, _) | AccountCommand::UnfreezeToken(id) | AccountCommand::ChangeTokenAuthority(id, _) => AccountType::Token(id), - AccountCommand::CancelOrder(id) | AccountCommand::FillOrder(id, _, _) => { + AccountCommand::ConcludeOrder(id) | AccountCommand::FillOrder(id, _, _) => { AccountType::Order(id) } } @@ -116,7 +116,7 @@ pub enum AccountCommand { #[codec(index = 5)] ChangeTokenAuthority(TokenId, Destination), #[codec(index = 6)] - CancelOrder(OrderId), + ConcludeOrder(OrderId), #[codec(index = 7)] FillOrder(OrderId, OutputValue, Destination), } diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs index 1d4e0a7c1e..9ae34c9ecf 100644 --- a/common/src/chain/transaction/output/mod.rs +++ b/common/src/chain/transaction/output/mod.rs @@ -331,8 +331,8 @@ impl TextSummary for TxOutput { ) } TxOutput::AnyoneCanTake(order) => format!( - "AnyoneCanTake(CancelKey({}), AskValue({}), GiveValue({}))", - fmt_dest(order.cancel_key()), + "AnyoneCanTake(ConcludeKey({}), AskValue({}), GiveValue({}))", + fmt_dest(order.conclude_key()), fmt_val(order.ask()), fmt_val(order.give()), ), diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index 11b295eb1a..868785827e 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -510,18 +510,18 @@ impl MempoolBanScore for orders_accounting::Error { Error::InvariantOrderAskBalanceChangedForUndo(_) => 100, Error::InvariantOrderGiveBalanceNotFoundForUndo(_) => 100, Error::InvariantOrderGiveBalanceChangedForUndo(_) => 100, - Error::InvariantOrderDataExistForCancelUndo(_) => 100, - Error::InvariantOrderAskBalanceExistForCancelUndo(_) => 100, - Error::InvariantOrderGiveBalanceExistForCancelUndo(_) => 100, + Error::InvariantOrderDataExistForConcludeUndo(_) => 100, + Error::InvariantOrderAskBalanceExistForConcludeUndo(_) => 100, + Error::InvariantOrderGiveBalanceExistForConcludeUndo(_) => 100, Error::InvariantNonzeroAskBalanceForMissingOrder(_) => 100, Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => 100, Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, Error::OrderOverbid(_, _, _) => 100, - Error::AttemptedCancelNonexistingOrderData(_) => 0, - Error::AttemptedCancelNonexistingAskBalance(_) => 0, - Error::AttemptedCancelNonexistingGiveBalance(_) => 0, + Error::AttemptedConcludeNonexistingOrderData(_) => 0, + Error::AttemptedConcludeNonexistingAskBalance(_) => 0, + Error::AttemptedConcludeNonexistingGiveBalance(_) => 0, Error::UnsupportedTokenVersion => 100, Error::ViewFail => 0, Error::StorageWrite => 0, diff --git a/mempool/src/pool/entry.rs b/mempool/src/pool/entry.rs index ab2055278d..ab69831eeb 100644 --- a/mempool/src/pool/entry.rs +++ b/mempool/src/pool/entry.rs @@ -75,7 +75,7 @@ impl TxDependency { | AccountCommand::ChangeTokenAuthority(_, _) => { Self::TokenSupplyAccount(TxAccountDependency::new(acct.clone().into(), nonce)) } - AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { + AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => { Self::OrderAccount(TxAccountDependency::new(acct.clone().into(), nonce)) } } diff --git a/mintscript/src/tests/translate/mod.rs b/mintscript/src/tests/translate/mod.rs index b6bf4ecc62..d8b70c093f 100644 --- a/mintscript/src/tests/translate/mod.rs +++ b/mintscript/src/tests/translate/mod.rs @@ -248,8 +248,8 @@ fn mint(id: TokenId, amount: u128) -> TestInputInfo { TestInputInfo::AccountCommand { command } } -fn cancel_order(id: OrderId) -> TestInputInfo { - let command = AccountCommand::CancelOrder(id); +fn conclude_order(id: OrderId) -> TestInputInfo { + let command = AccountCommand::ConcludeOrder(id); TestInputInfo::AccountCommand { command } } @@ -353,10 +353,10 @@ fn mode_name<'a, T: TranslationMode<'a>>(_: &T) -> &'static str { #[case("htlc_04", htlc(19, 20, tl_for_blocks(1000)), htlc_multisig(0x55))] #[case("anyonecantake_00", anyonecantake(order0().1), nosig())] #[case("anyonecantake_01", anyonecantake(order0().1), stdsig(0x57))] -#[case("cancelorder_00", cancel_order(order0().0), nosig())] -#[case("cancelorder_01", cancel_order(fake_id(0x88)), nosig())] -#[case("cancelorder_02", cancel_order(order0().0), stdsig(0x44))] -#[case("cancelorder_03", cancel_order(order0().0), stdsig(0x45))] +#[case("concludeorder_00", conclude_order(order0().0), nosig())] +#[case("concludeorder_01", conclude_order(fake_id(0x88)), nosig())] +#[case("concludeorder_02", conclude_order(order0().0), stdsig(0x44))] +#[case("concludeorder_03", conclude_order(order0().0), stdsig(0x45))] #[case("fillorder_00", fill_order(order0().0), nosig())] #[case("fillorder_01", fill_order(fake_id(0x77)), nosig())] #[case("fillorder_00", fill_order(order0().0), stdsig(0x45))] diff --git a/mintscript/src/tests/translate/snap.translate.reward.cancelorder_00.txt b/mintscript/src/tests/translate/snap.translate.reward.concludeorder_00.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.reward.cancelorder_00.txt rename to mintscript/src/tests/translate/snap.translate.reward.concludeorder_00.txt diff --git a/mintscript/src/tests/translate/snap.translate.reward.cancelorder_01.txt b/mintscript/src/tests/translate/snap.translate.reward.concludeorder_01.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.reward.cancelorder_01.txt rename to mintscript/src/tests/translate/snap.translate.reward.concludeorder_01.txt diff --git a/mintscript/src/tests/translate/snap.translate.reward.cancelorder_02.txt b/mintscript/src/tests/translate/snap.translate.reward.concludeorder_02.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.reward.cancelorder_02.txt rename to mintscript/src/tests/translate/snap.translate.reward.concludeorder_02.txt diff --git a/mintscript/src/tests/translate/snap.translate.reward.cancelorder_03.txt b/mintscript/src/tests/translate/snap.translate.reward.concludeorder_03.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.reward.cancelorder_03.txt rename to mintscript/src/tests/translate/snap.translate.reward.concludeorder_03.txt diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_00.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.concludeorder_00.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_00.txt rename to mintscript/src/tests/translate/snap.translate.tlockonly.concludeorder_00.txt diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_01.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.concludeorder_01.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_01.txt rename to mintscript/src/tests/translate/snap.translate.tlockonly.concludeorder_01.txt diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_02.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.concludeorder_02.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_02.txt rename to mintscript/src/tests/translate/snap.translate.tlockonly.concludeorder_02.txt diff --git a/mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_03.txt b/mintscript/src/tests/translate/snap.translate.tlockonly.concludeorder_03.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.tlockonly.cancelorder_03.txt rename to mintscript/src/tests/translate/snap.translate.tlockonly.concludeorder_03.txt diff --git a/mintscript/src/tests/translate/snap.translate.txn.cancelorder_00.txt b/mintscript/src/tests/translate/snap.translate.txn.concludeorder_00.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.txn.cancelorder_00.txt rename to mintscript/src/tests/translate/snap.translate.txn.concludeorder_00.txt diff --git a/mintscript/src/tests/translate/snap.translate.txn.cancelorder_01.txt b/mintscript/src/tests/translate/snap.translate.txn.concludeorder_01.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.txn.cancelorder_01.txt rename to mintscript/src/tests/translate/snap.translate.txn.concludeorder_01.txt diff --git a/mintscript/src/tests/translate/snap.translate.txn.cancelorder_02.txt b/mintscript/src/tests/translate/snap.translate.txn.concludeorder_02.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.txn.cancelorder_02.txt rename to mintscript/src/tests/translate/snap.translate.txn.concludeorder_02.txt diff --git a/mintscript/src/tests/translate/snap.translate.txn.cancelorder_03.txt b/mintscript/src/tests/translate/snap.translate.txn.concludeorder_03.txt similarity index 100% rename from mintscript/src/tests/translate/snap.translate.txn.cancelorder_03.txt rename to mintscript/src/tests/translate/snap.translate.txn.concludeorder_03.txt diff --git a/mintscript/src/translate.rs b/mintscript/src/translate.rs index 9bd419009d..ca590096be 100644 --- a/mintscript/src/translate.rs +++ b/mintscript/src/translate.rs @@ -215,12 +215,12 @@ impl TranslateInput for SignedTransaction { }; Ok(checksig(dest)) } - AccountCommand::CancelOrder(order_id) => { + AccountCommand::ConcludeOrder(order_id) => { let order_data = ctx .orders() .get_order_data(order_id)? .ok_or(TranslationError::OrderNotFound(*order_id))?; - Ok(checksig(order_data.cancel_key())) + Ok(checksig(order_data.conclude_key())) } AccountCommand::FillOrder(_, _, _) => Ok(WitnessScript::TRUE), }, @@ -314,7 +314,7 @@ impl TranslateInput for TimelockOnly { | AccountCommand::FreezeToken(_token_id, _) | AccountCommand::UnfreezeToken(_token_id) | AccountCommand::ChangeTokenAuthority(_token_id, _) => Ok(WitnessScript::TRUE), - AccountCommand::CancelOrder(_) | AccountCommand::FillOrder(_, _, _) => { + AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => { Ok(WitnessScript::TRUE) } }, diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index 23d45d4305..bbf671f371 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -26,7 +26,7 @@ use crate::{ data::OrdersAccountingDeltaData, error::{Error, Result}, operations::{ - CancelOrderUndo, CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, + ConcludeOrderUndo, CreateOrderUndo, FillOrderUndo, OrdersAccountingOperations, OrdersAccountingUndo, }, view::OrdersAccountingView, @@ -91,22 +91,22 @@ impl OrdersAccountingCache

{ Ok(()) } - fn undo_cancel_order(&mut self, undo: CancelOrderUndo) -> Result<()> { + fn undo_conclude_order(&mut self, undo: ConcludeOrderUndo) -> Result<()> { ensure!( self.get_order_data(&undo.id)?.is_none(), - Error::InvariantOrderDataExistForCancelUndo(undo.id) + Error::InvariantOrderDataExistForConcludeUndo(undo.id) ); self.data.order_data.undo_merge_delta_data_element(undo.id, undo.undo_data)?; ensure!( self.get_ask_balance(&undo.id)?.unwrap_or(Amount::ZERO) == Amount::ZERO, - Error::InvariantOrderAskBalanceExistForCancelUndo(undo.id) + Error::InvariantOrderAskBalanceExistForConcludeUndo(undo.id) ); self.data.ask_balances.add_unsigned(undo.id, undo.ask_balance)?; ensure!( self.get_give_balance(&undo.id)?.unwrap_or(Amount::ZERO) == Amount::ZERO, - Error::InvariantOrderGiveBalanceExistForCancelUndo(undo.id) + Error::InvariantOrderGiveBalanceExistForConcludeUndo(undo.id) ); self.data.give_balances.add_unsigned(undo.id, undo.give_balance)?; @@ -117,7 +117,7 @@ impl OrdersAccountingCache

{ if let Some(undo_data) = undo.undo_data { ensure!( self.get_order_data(&undo.id)?.is_none(), - Error::InvariantOrderDataExistForCancelUndo(undo.id) + Error::InvariantOrderDataExistForConcludeUndo(undo.id) ); self.data.order_data.undo_merge_delta_data_element(undo.id, undo_data)?; } @@ -219,18 +219,18 @@ impl OrdersAccountingOperations for OrdersAccountingCac })) } - fn cancel_order(&mut self, id: OrderId) -> Result { - log::debug!("Canceling an order: {:?}", id); + fn conclude_order(&mut self, id: OrderId) -> Result { + log::debug!("Concluding an order: {:?}", id); let order_data = self .get_order_data(&id)? - .ok_or(Error::AttemptedCancelNonexistingOrderData(id))?; + .ok_or(Error::AttemptedConcludeNonexistingOrderData(id))?; let ask_balance = self .get_ask_balance(&id)? - .ok_or(Error::AttemptedCancelNonexistingAskBalance(id))?; + .ok_or(Error::AttemptedConcludeNonexistingAskBalance(id))?; let give_balance = self .get_give_balance(&id)? - .ok_or(Error::AttemptedCancelNonexistingGiveBalance(id))?; + .ok_or(Error::AttemptedConcludeNonexistingGiveBalance(id))?; let undo_data = self .data @@ -240,7 +240,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac self.data.ask_balances.sub_unsigned(id, ask_balance)?; self.data.give_balances.sub_unsigned(id, give_balance)?; - Ok(OrdersAccountingUndo::CancelOrder(CancelOrderUndo { + Ok(OrdersAccountingUndo::ConcludeOrder(ConcludeOrderUndo { id, undo_data, ask_balance, @@ -290,7 +290,7 @@ impl OrdersAccountingOperations for OrdersAccountingCac log::debug!("Undo an order: {:?}", undo_data); match undo_data { OrdersAccountingUndo::CreateOrder(undo) => self.undo_create_order(undo), - OrdersAccountingUndo::CancelOrder(undo) => self.undo_cancel_order(undo), + OrdersAccountingUndo::ConcludeOrder(undo) => self.undo_conclude_order(undo), OrdersAccountingUndo::FillOrder(undo) => self.undo_fill_order(undo), } } diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index c14ef175af..84290626a8 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -41,12 +41,12 @@ pub enum Error { InvariantOrderGiveBalanceNotFoundForUndo(OrderId), #[error("Give balance for order {0}` changed for undo")] InvariantOrderGiveBalanceChangedForUndo(OrderId), - #[error("Data for order {0}` still exist on cancel undo")] - InvariantOrderDataExistForCancelUndo(OrderId), - #[error("Ask balance for order {0}` still exist on cancel undo")] - InvariantOrderAskBalanceExistForCancelUndo(OrderId), - #[error("Give balance for order {0}` still exist on cancel undo")] - InvariantOrderGiveBalanceExistForCancelUndo(OrderId), + #[error("Data for order {0}` still exist on conclude undo")] + InvariantOrderDataExistForConcludeUndo(OrderId), + #[error("Ask balance for order {0}` still exist on conclude undo")] + InvariantOrderAskBalanceExistForConcludeUndo(OrderId), + #[error("Give balance for order {0}` still exist on conclude undo")] + InvariantOrderGiveBalanceExistForConcludeUndo(OrderId), #[error("Ask balance for non-existing order {0}` is not zero")] InvariantNonzeroAskBalanceForMissingOrder(OrderId), #[error("Give balance for non-existing order {0}` is not zero")] @@ -59,12 +59,12 @@ pub enum Error { OrderOverflow(OrderId), #[error("Order `{0}` can provide `{1:?}` amount; but attempted to fill `{2:?}`")] OrderOverbid(OrderId, Amount, Amount), - #[error("Attempt to cancel non-existing order data `{0}`")] - AttemptedCancelNonexistingOrderData(OrderId), - #[error("Attempt to cancel non-existing ask balance `{0}`")] - AttemptedCancelNonexistingAskBalance(OrderId), - #[error("Attempt to cancel non-existing give balance `{0}`")] - AttemptedCancelNonexistingGiveBalance(OrderId), + #[error("Attempt to conclude non-existing order data `{0}`")] + AttemptedConcludeNonexistingOrderData(OrderId), + #[error("Attempt to conclude non-existing ask balance `{0}`")] + AttemptedConcludeNonexistingAskBalance(OrderId), + #[error("Attempt to conclude non-existing give balance `{0}`")] + AttemptedConcludeNonexistingGiveBalance(OrderId), #[error("Unsupported token version")] UnsupportedTokenVersion, diff --git a/orders-accounting/src/operations.rs b/orders-accounting/src/operations.rs index 0de37bd24d..a6e758d6fc 100644 --- a/orders-accounting/src/operations.rs +++ b/orders-accounting/src/operations.rs @@ -32,7 +32,7 @@ pub struct CreateOrderUndo { } #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] -pub struct CancelOrderUndo { +pub struct ConcludeOrderUndo { pub(crate) id: OrderId, pub(crate) undo_data: DataDeltaUndo, pub(crate) ask_balance: Amount, @@ -51,13 +51,13 @@ pub struct FillOrderUndo { #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, VariantCount)] pub enum OrdersAccountingUndo { CreateOrder(CreateOrderUndo), - CancelOrder(CancelOrderUndo), + ConcludeOrder(ConcludeOrderUndo), FillOrder(FillOrderUndo), } pub trait OrdersAccountingOperations { fn create_order(&mut self, id: OrderId, data: OrderData) -> Result; - fn cancel_order(&mut self, id: OrderId) -> Result; + fn conclude_order(&mut self, id: OrderId) -> Result; fn fill_order(&mut self, id: OrderId, value: OutputValue) -> Result; fn undo(&mut self, undo_data: OrdersAccountingUndo) -> Result<()>; diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index 350d71f477..b8c250c757 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -151,7 +151,7 @@ fn create_order_and_undo(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn cancel_order_and_flush(#[case] seed: Seed) { +fn conclude_order_and_flush(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let order_id = OrderId::random_using(&mut rng); @@ -165,17 +165,17 @@ fn cancel_order_and_flush(#[case] seed: Seed) { let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); - // try to cancel non-existing order + // try to conclude non-existing order { let random_order = OrderId::random_using(&mut rng); - let result = cache.cancel_order(random_order); + let result = cache.conclude_order(random_order); assert_eq!( result.unwrap_err(), - Error::AttemptedCancelNonexistingOrderData(random_order) + Error::AttemptedConcludeNonexistingOrderData(random_order) ); } - let _ = cache.cancel_order(order_id).unwrap(); + let _ = cache.conclude_order(order_id).unwrap(); db.batch_write_orders_data(cache.consume()).unwrap(); @@ -185,7 +185,7 @@ fn cancel_order_and_flush(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn cancel_order_twice(#[case] seed: Seed) { +fn conclude_order_twice(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let order_id = OrderId::random_using(&mut rng); @@ -199,18 +199,18 @@ fn cancel_order_twice(#[case] seed: Seed) { let db = OrdersAccountingDB::new(&storage); let mut cache = OrdersAccountingCache::new(&db); - let _ = cache.cancel_order(order_id).unwrap(); + let _ = cache.conclude_order(order_id).unwrap(); assert_eq!( - cache.cancel_order(order_id,), - Err(Error::AttemptedCancelNonexistingOrderData(order_id)) + cache.conclude_order(order_id,), + Err(Error::AttemptedConcludeNonexistingOrderData(order_id)) ); } #[rstest] #[trace] #[case(Seed::from_entropy())] -fn cancel_order_and_undo(#[case] seed: Seed) { +fn conclude_order_and_undo(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let order_id = OrderId::random_using(&mut rng); @@ -225,7 +225,7 @@ fn cancel_order_and_undo(#[case] seed: Seed) { let mut db = OrdersAccountingDB::new(&mut storage); let mut cache = OrdersAccountingCache::new(&db); - let undo = cache.cancel_order(order_id).unwrap(); + let undo = cache.conclude_order(order_id).unwrap(); assert_eq!(None, cache.get_order_data(&order_id).unwrap().as_ref()); assert_eq!(None, cache.get_ask_balance(&order_id).unwrap()); @@ -506,7 +506,7 @@ fn fill_order_partially_and_undo(#[case] seed: Seed) { #[rstest] #[trace] #[case(Seed::from_entropy())] -fn fill_order_partially_and_cancel(#[case] seed: Seed) { +fn fill_order_partially_and_conclude(#[case] seed: Seed) { let mut rng = make_seedable_rng(seed); let order_id = OrderId::random_using(&mut rng); @@ -545,7 +545,7 @@ fn fill_order_partially_and_cancel(#[case] seed: Seed) { cache.get_give_balance(&order_id).unwrap() ); - let _ = cache.cancel_order(order_id).unwrap(); + let _ = cache.conclude_order(order_id).unwrap(); db.batch_write_orders_data(cache.consume()).unwrap(); diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index 920aa9cc17..ebead6cb95 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -1348,7 +1348,7 @@ impl Account { .map(|data| (None, Some(data.authority.clone()))) .ok_or(WalletError::UnknownTokenId(*token_id)), // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), } } @@ -1829,7 +1829,7 @@ impl Account { || self.is_destination_mine_or_watched(address) } // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, }); @@ -2240,7 +2240,7 @@ fn group_preselected_inputs( )?; } // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index 4f2193472b..7484a344c4 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -670,7 +670,7 @@ impl OutputCache { | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) | AccountCommand::UnfreezeToken(_) - | AccountCommand::CancelOrder(_) + | AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => None, AccountCommand::FreezeToken(frozen_token_id, _) => Some(frozen_token_id), }, @@ -733,7 +733,7 @@ impl OutputCache { | AccountCommand::ChangeTokenAuthority(token_id, _) | AccountCommand::UnmintTokens(token_id) => frozen_token_id == token_id, // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, TxInput::Account(_) => false, @@ -928,7 +928,7 @@ impl OutputCache { } } // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } @@ -1031,7 +1031,7 @@ impl OutputCache { } } // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } @@ -1324,7 +1324,7 @@ impl OutputCache { } } // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } @@ -1533,7 +1533,7 @@ fn apply_freeze_mutations_from_tx( | AccountCommand::LockTokenSupply(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } @@ -1575,7 +1575,7 @@ fn apply_total_supply_mutations_from_tx( | AccountCommand::UnfreezeToken(_) | AccountCommand::ChangeTokenAuthority(_, _) => {} // TODO(orders) - AccountCommand::CancelOrder(_) => unimplemented!(), + AccountCommand::ConcludeOrder(_) => unimplemented!(), AccountCommand::FillOrder(_, _, _) => unimplemented!(), }, } From 2ba828d626503817b14148c89d9ec8982cb24a14 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Wed, 26 Jun 2024 17:26:35 +0300 Subject: [PATCH 36/37] Fix complete order fill --- .../src/constraints_accumulator.rs | 4 +- chainstate/src/detail/ban_score.rs | 3 - chainstate/src/detail/error_classification.rs | 3 - .../test-suite/src/tests/orders_tests.rs | 266 +++++++++++++++++- mempool/src/error/ban_score.rs | 3 - orders-accounting/src/cache.rs | 38 +-- orders-accounting/src/error.rs | 6 - orders-accounting/src/operations.rs | 1 - orders-accounting/src/tests/operations.rs | 43 ++- 9 files changed, 303 insertions(+), 64 deletions(-) diff --git a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs index aac489a45f..2672c1513e 100644 --- a/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs +++ b/chainstate/constraints-value-accumulator/src/constraints_accumulator.rs @@ -274,11 +274,11 @@ impl ConstrainedValueAccumulator { let ask_balance = orders_accounting_view .get_ask_balance(id) .map_err(|_| orders_accounting::Error::ViewFail)? - .ok_or(orders_accounting::Error::OrderAskBalanceNotFound(*id))?; + .unwrap_or(Amount::ZERO); let give_balance = orders_accounting_view .get_give_balance(id) .map_err(|_| orders_accounting::Error::ViewFail)? - .ok_or(orders_accounting::Error::OrderGiveBalanceNotFound(*id))?; + .unwrap_or(Amount::ZERO); let initially_asked = output_value_amount(order_data.ask())?; let filled_amount = (initially_asked - ask_balance) diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 24e67f3e04..15b9245bb2 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -675,13 +675,10 @@ impl BanScore for orders_accounting::Error { Error::InvariantOrderDataExistForConcludeUndo(_) => 100, Error::InvariantOrderAskBalanceExistForConcludeUndo(_) => 100, Error::InvariantOrderGiveBalanceExistForConcludeUndo(_) => 100, - Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, Error::OrderOverbid(_, _, _) => 100, Error::AttemptedConcludeNonexistingOrderData(_) => 100, - Error::AttemptedConcludeNonexistingAskBalance(_) => 100, - Error::AttemptedConcludeNonexistingGiveBalance(_) => 100, Error::InvariantNonzeroAskBalanceForMissingOrder(_) => 100, Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => 100, Error::UnsupportedTokenVersion => 100, diff --git a/chainstate/src/detail/error_classification.rs b/chainstate/src/detail/error_classification.rs index 65608a6ea1..b047f299bd 100644 --- a/chainstate/src/detail/error_classification.rs +++ b/chainstate/src/detail/error_classification.rs @@ -911,13 +911,10 @@ impl BlockProcessingErrorClassification for orders_accounting::Error { | Error::InvariantOrderDataExistForConcludeUndo(_) | Error::InvariantOrderAskBalanceExistForConcludeUndo(_) | Error::InvariantOrderGiveBalanceExistForConcludeUndo(_) - | Error::FillOrderChangeLeft(_) | Error::CurrencyMismatch | Error::OrderOverflow(_) | Error::OrderOverbid(_, _, _) | Error::AttemptedConcludeNonexistingOrderData(_) - | Error::AttemptedConcludeNonexistingAskBalance(_) - | Error::AttemptedConcludeNonexistingGiveBalance(_) | Error::UnsupportedTokenVersion | Error::InvariantNonzeroAskBalanceForMissingOrder(_) | Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => { diff --git a/chainstate/test-suite/src/tests/orders_tests.rs b/chainstate/test-suite/src/tests/orders_tests.rs index 1555414a09..2efa95f2c8 100644 --- a/chainstate/test-suite/src/tests/orders_tests.rs +++ b/chainstate/test-suite/src/tests/orders_tests.rs @@ -29,7 +29,7 @@ use common::{ AccountCommand, AccountNonce, ChainstateUpgrade, Destination, OrderData, SignedTransaction, TxInput, TxOutput, UtxoOutPoint, }, - primitives::{Amount, BlockHeight, Idable}, + primitives::{Amount, BlockHeight, CoinOrTokenId, Idable}, }; use crypto::key::{KeyKind, PrivateKey}; use orders_accounting::OrdersAccountingDB; @@ -511,7 +511,7 @@ fn fill_order_check_storage(#[case] seed: Seed) { tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); assert_eq!( - Some(order_data), + Some(order_data.clone()), tf.chainstate.get_order_data(&order_id).unwrap() ); assert_eq!( @@ -550,7 +550,10 @@ fn fill_order_check_storage(#[case] seed: Seed) { .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); - assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap()); + assert_eq!( + Some(order_data), + tf.chainstate.get_order_data(&order_id).unwrap() + ); assert_eq!( None, tf.chainstate.get_order_ask_balance(&order_id).unwrap() @@ -618,6 +621,79 @@ fn fill_partially_then_conclude(#[case] seed: Seed) { .build(); tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + { + // Try overspend give in conclude order + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(1), + AccountCommand::ConcludeOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1( + token_id, + (give_amount - filled_amount) + .and_then(|v| v + Amount::from_atoms(1)) + .unwrap(), + ), + Destination::AnyoneCanSpend, + )) + .add_output(TxOutput::Transfer( + OutputValue::Coin(output_value_amount(&fill_value)), + Destination::AnyoneCanSpend, + )) + .build(); + let tx_id = tx.transaction().get_id(); + let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + assert_eq!(res.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError(chainstate::BlockError::StateUpdateFailed( + ConnectTransactionError::ConstrainedValueAccumulatorError( + constraints_value_accumulator::Error::AttemptToPrintMoneyOrViolateTimelockConstraints( + CoinOrTokenId::TokenId(token_id) + ), + tx_id.into() + ) + )) + ); + } + + { + // Try overspend ask in conclude order + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(1), + AccountCommand::ConcludeOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, (give_amount - filled_amount).unwrap()), + Destination::AnyoneCanSpend, + )) + .add_output(TxOutput::Transfer( + OutputValue::Coin( + (output_value_amount(&fill_value) + Amount::from_atoms(1)).unwrap(), + ), + Destination::AnyoneCanSpend, + )) + .build(); + let tx_id = tx.transaction().get_id(); + let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + assert_eq!(res.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError(chainstate::BlockError::StateUpdateFailed( + ConnectTransactionError::ConstrainedValueAccumulatorError( + constraints_value_accumulator::Error::AttemptToPrintMoneyOrViolateTimelockConstraints( + CoinOrTokenId::Coin + ), + tx_id.into() + ) + )) + ); + } + // conclude the order let tx = TransactionBuilder::new() .add_input( @@ -650,6 +726,190 @@ fn fill_partially_then_conclude(#[case] seed: Seed) { }); } +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn fill_completely_then_conclude(#[case] seed: Seed) { + utils::concurrency::model(move || { + let mut rng = make_seedable_rng(seed); + let mut tf = TestFramework::builder(&mut rng).build(); + + let (token_id, tokens_outpoint, coins_outpoint) = + issue_and_mint_token_from_genesis(&mut rng, &mut tf); + + let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000)); + let order_data = OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(ask_amount), + OutputValue::TokenV1(token_id, give_amount), + ); + + let order_id = make_order_id(&tokens_outpoint); + let tx = TransactionBuilder::new() + .add_input(tokens_outpoint.into(), InputWitness::NoSignature(None)) + .add_output(TxOutput::AnyoneCanTake(Box::new(order_data))) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + { + // Try overspend complete fill order + let tx = TransactionBuilder::new() + .add_input( + coins_outpoint.clone().into(), + InputWitness::NoSignature(None), + ) + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder( + order_id, + OutputValue::Coin(ask_amount), + Destination::AnyoneCanSpend, + ), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, (give_amount + Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, + )) + .build(); + let tx_id = tx.transaction().get_id(); + let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + assert_eq!(res.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError(chainstate::BlockError::StateUpdateFailed( + ConnectTransactionError::ConstrainedValueAccumulatorError( + constraints_value_accumulator::Error::AttemptToPrintMoneyOrViolateTimelockConstraints( + CoinOrTokenId::TokenId(token_id) + ), + tx_id.into() + ) + )) + ); + } + + { + // Try overbid complete fill order + let tx = TransactionBuilder::new() + .add_input( + coins_outpoint.clone().into(), + InputWitness::NoSignature(None), + ) + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder( + order_id, + OutputValue::Coin((ask_amount + Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, + ), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, give_amount), + Destination::AnyoneCanSpend, + )) + .build(); + let tx_id = tx.transaction().get_id(); + let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + assert_eq!( + res.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError( + chainstate::BlockError::StateUpdateFailed( + ConnectTransactionError::ConstrainedValueAccumulatorError( + orders_accounting::Error::OrderOverbid( + order_id, + ask_amount, + (ask_amount + Amount::from_atoms(1)).unwrap() + ) + .into(), + tx_id.into() + ) + ) + ) + ); + } + + // Fill the order completely + let tx = TransactionBuilder::new() + .add_input(coins_outpoint.into(), InputWitness::NoSignature(None)) + .add_input( + TxInput::AccountCommand( + AccountNonce::new(0), + AccountCommand::FillOrder( + order_id, + OutputValue::Coin(ask_amount), + Destination::AnyoneCanSpend, + ), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::TokenV1(token_id, give_amount), + Destination::AnyoneCanSpend, + )) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + { + // Try overspend conclude order + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(1), + AccountCommand::ConcludeOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::Coin((ask_amount + Amount::from_atoms(1)).unwrap()), + Destination::AnyoneCanSpend, + )) + .build(); + let tx_id = tx.transaction().get_id(); + let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng); + assert_eq!(res.unwrap_err(), + chainstate::ChainstateError::ProcessBlockError(chainstate::BlockError::StateUpdateFailed( + ConnectTransactionError::ConstrainedValueAccumulatorError( + constraints_value_accumulator::Error::AttemptToPrintMoneyOrViolateTimelockConstraints( + CoinOrTokenId::Coin + ), + tx_id.into() + ) + )) + ); + } + + // conclude the order + let tx = TransactionBuilder::new() + .add_input( + TxInput::AccountCommand( + AccountNonce::new(1), + AccountCommand::ConcludeOrder(order_id), + ), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::Coin(ask_amount), + Destination::AnyoneCanSpend, + )) + .build(); + tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap(); + + assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap()); + assert_eq!( + None, + tf.chainstate.get_order_ask_balance(&order_id).unwrap() + ); + assert_eq!( + None, + tf.chainstate.get_order_give_balance(&order_id).unwrap() + ); + }); +} + #[rstest] #[trace] #[case(Seed::from_entropy())] diff --git a/mempool/src/error/ban_score.rs b/mempool/src/error/ban_score.rs index 868785827e..f973144a54 100644 --- a/mempool/src/error/ban_score.rs +++ b/mempool/src/error/ban_score.rs @@ -515,13 +515,10 @@ impl MempoolBanScore for orders_accounting::Error { Error::InvariantOrderGiveBalanceExistForConcludeUndo(_) => 100, Error::InvariantNonzeroAskBalanceForMissingOrder(_) => 100, Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => 100, - Error::FillOrderChangeLeft(_) => 100, Error::CurrencyMismatch => 100, Error::OrderOverflow(_) => 100, Error::OrderOverbid(_, _, _) => 100, Error::AttemptedConcludeNonexistingOrderData(_) => 0, - Error::AttemptedConcludeNonexistingAskBalance(_) => 0, - Error::AttemptedConcludeNonexistingGiveBalance(_) => 0, Error::UnsupportedTokenVersion => 100, Error::ViewFail => 0, Error::StorageWrite => 0, diff --git a/orders-accounting/src/cache.rs b/orders-accounting/src/cache.rs index bbf671f371..426c3bc001 100644 --- a/orders-accounting/src/cache.rs +++ b/orders-accounting/src/cache.rs @@ -114,14 +114,6 @@ impl OrdersAccountingCache

{ } fn undo_fill_order(&mut self, undo: FillOrderUndo) -> Result<()> { - if let Some(undo_data) = undo.undo_data { - ensure!( - self.get_order_data(&undo.id)?.is_none(), - Error::InvariantOrderDataExistForConcludeUndo(undo.id) - ); - self.data.order_data.undo_merge_delta_data_element(undo.id, undo_data)?; - } - self.data.ask_balances.add_unsigned(undo.id, undo.ask_balance)?; self.data.give_balances.add_unsigned(undo.id, undo.give_balance)?; @@ -225,12 +217,8 @@ impl OrdersAccountingOperations for OrdersAccountingCac let order_data = self .get_order_data(&id)? .ok_or(Error::AttemptedConcludeNonexistingOrderData(id))?; - let ask_balance = self - .get_ask_balance(&id)? - .ok_or(Error::AttemptedConcludeNonexistingAskBalance(id))?; - let give_balance = self - .get_give_balance(&id)? - .ok_or(Error::AttemptedConcludeNonexistingGiveBalance(id))?; + let ask_balance = self.get_ask_balance(&id)?.unwrap_or(Amount::ZERO); + let give_balance = self.get_give_balance(&id)?.unwrap_or(Amount::ZERO); let undo_data = self .data @@ -254,33 +242,11 @@ impl OrdersAccountingOperations for OrdersAccountingCac let fill_amount = output_value_amount(&fill_value)?; let filled_amount = calculate_fill_order(self, id, &fill_value)?; - let ask_balance = self.get_ask_balance(&id)?.ok_or(Error::OrderAskBalanceNotFound(id))?; - let give_balance = - self.get_give_balance(&id)?.ok_or(Error::OrderGiveBalanceNotFound(id))?; - - // in case the order is completely filled it can be removed - let undo_data = if fill_amount == ask_balance { - ensure!( - filled_amount == give_balance, - Error::FillOrderChangeLeft(id) - ); - - let order_data = self.get_order_data(&id)?.ok_or(Error::OrderDataNotFound(id))?; - let undo = self - .data - .order_data - .merge_delta_data_element(id, accounting::DataDelta::new(Some(order_data), None))?; - Some(undo) - } else { - None - }; - self.data.give_balances.sub_unsigned(id, filled_amount)?; self.data.ask_balances.sub_unsigned(id, fill_amount)?; Ok(OrdersAccountingUndo::FillOrder(FillOrderUndo { id, - undo_data, ask_balance: fill_amount, give_balance: filled_amount, })) diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index 84290626a8..a14a78344b 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -51,8 +51,6 @@ pub enum Error { InvariantNonzeroAskBalanceForMissingOrder(OrderId), #[error("Give balance for non-existing order {0}` is not zero")] InvariantNonzeroGiveBalanceForMissingOrder(OrderId), - #[error("Fill operation for order {0}` left a change")] - FillOrderChangeLeft(OrderId), #[error("Coin type mismatch")] CurrencyMismatch, #[error("Order overflow: `{0}`")] @@ -61,10 +59,6 @@ pub enum Error { OrderOverbid(OrderId, Amount, Amount), #[error("Attempt to conclude non-existing order data `{0}`")] AttemptedConcludeNonexistingOrderData(OrderId), - #[error("Attempt to conclude non-existing ask balance `{0}`")] - AttemptedConcludeNonexistingAskBalance(OrderId), - #[error("Attempt to conclude non-existing give balance `{0}`")] - AttemptedConcludeNonexistingGiveBalance(OrderId), #[error("Unsupported token version")] UnsupportedTokenVersion, diff --git a/orders-accounting/src/operations.rs b/orders-accounting/src/operations.rs index a6e758d6fc..42be5efcca 100644 --- a/orders-accounting/src/operations.rs +++ b/orders-accounting/src/operations.rs @@ -42,7 +42,6 @@ pub struct ConcludeOrderUndo { #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)] pub struct FillOrderUndo { pub(crate) id: OrderId, - pub(crate) undo_data: Option>, pub(crate) ask_balance: Amount, pub(crate) give_balance: Amount, } diff --git a/orders-accounting/src/tests/operations.rs b/orders-accounting/src/tests/operations.rs index b8c250c757..b03210fef2 100644 --- a/orders-accounting/src/tests/operations.rs +++ b/orders-accounting/src/tests/operations.rs @@ -282,7 +282,12 @@ fn fill_order_wrong_currency(#[case] seed: Seed) { db.batch_write_orders_data(cache.consume()).unwrap(); - assert_eq!(InMemoryOrdersAccounting::new(), storage); + let expected_storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data)]), + BTreeMap::new(), + BTreeMap::new(), + ); + assert_eq!(expected_storage, storage); } #[rstest] @@ -328,7 +333,12 @@ fn fill_entire_order_and_flush(#[case] seed: Seed) { db.batch_write_orders_data(cache.consume()).unwrap(); - assert_eq!(InMemoryOrdersAccounting::new(), storage); + let expected_storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data)]), + BTreeMap::new(), + BTreeMap::new(), + ); + assert_eq!(expected_storage, storage); } #[rstest] @@ -402,7 +412,12 @@ fn fill_order_partially_and_flush(#[case] seed: Seed) { db.batch_write_orders_data(cache.consume()).unwrap(); - assert_eq!(InMemoryOrdersAccounting::new(), storage); + let expected_storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data)]), + BTreeMap::new(), + BTreeMap::new(), + ); + assert_eq!(expected_storage, storage); } #[rstest] @@ -449,9 +464,18 @@ fn fill_order_partially_and_undo(#[case] seed: Seed) { ) .unwrap(); - assert_eq!(None, cache.get_order_data(&order_id).unwrap().as_ref()); - assert_eq!(None, cache.get_ask_balance(&order_id).unwrap()); - assert_eq!(None, cache.get_give_balance(&order_id).unwrap()); + assert_eq!( + Some(&order_data), + cache.get_order_data(&order_id).unwrap().as_ref() + ); + assert_eq!( + Some(Amount::ZERO), + cache.get_ask_balance(&order_id).unwrap() + ); + assert_eq!( + Some(Amount::ZERO), + cache.get_give_balance(&order_id).unwrap() + ); cache.undo(undo3).unwrap(); @@ -583,5 +607,10 @@ fn fill_order_must_converge(#[case] seed: Seed) { db.batch_write_orders_data(cache.consume()).unwrap(); - assert_eq!(InMemoryOrdersAccounting::new(), storage); + let expected_storage = InMemoryOrdersAccounting::from_values( + BTreeMap::from_iter([(order_id, order_data)]), + BTreeMap::new(), + BTreeMap::new(), + ); + assert_eq!(expected_storage, storage); } From 323a56939953e1b19af747598fb802fb296e7c98 Mon Sep 17 00:00:00 2001 From: Heorhii Azarov Date: Thu, 27 Jun 2024 12:53:51 +0300 Subject: [PATCH 37/37] Fix review comments --- common/src/chain/tokens/issuance.rs | 2 +- orders-accounting/src/error.rs | 30 +++++++++--------- orders-accounting/src/price_calculation.rs | 36 ++++++++++++---------- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/common/src/chain/tokens/issuance.rs b/common/src/chain/tokens/issuance.rs index 6c2c8024c3..73c1699a70 100644 --- a/common/src/chain/tokens/issuance.rs +++ b/common/src/chain/tokens/issuance.rs @@ -101,7 +101,7 @@ impl IsTokenUnfreezable { /// Indicates whether a token is frozen at the moment or not. If it is then no operations with this token can be performed. /// Meaning transfers, burns, minting, unminting, supply locks etc. Frozen token can only be unfrozen -/// is such an option was provided while freezing. +/// if such an option was provided while freezing. #[derive( Debug, Copy, diff --git a/orders-accounting/src/error.rs b/orders-accounting/src/error.rs index a14a78344b..71c73c9f85 100644 --- a/orders-accounting/src/error.rs +++ b/orders-accounting/src/error.rs @@ -19,37 +19,37 @@ use common::{chain::OrderId, primitives::Amount}; pub enum Error { #[error("Accounting storage error")] StorageError(#[from] chainstate_types::storage_result::Error), - #[error("Base accounting error: {0}")] + #[error("Base accounting error: `{0}`")] AccountingError(#[from] accounting::Error), - #[error("Order already exist: `{0}`")] + #[error("Order already exists: `{0}`")] OrderAlreadyExists(OrderId), - #[error("Data for order {0}` not found")] + #[error("Data for order `{0}` not found")] OrderDataNotFound(OrderId), - #[error("Ask balance for order {0}` not found")] + #[error("Ask balance for order `{0}` not found")] OrderAskBalanceNotFound(OrderId), - #[error("Give balance for order {0}` not found")] + #[error("Give balance for order `{0}` not found")] OrderGiveBalanceNotFound(OrderId), #[error("Attempt to create an order with zero exchange value `{0}`")] OrderWithZeroValue(OrderId), - #[error("Data for order {0}` not found for undo")] + #[error("Data for order `{0}` not found for undo")] InvariantOrderDataNotFoundForUndo(OrderId), - #[error("Ask balance for order {0}` not found for undo")] + #[error("Ask balance for order `{0}` not found for undo")] InvariantOrderAskBalanceNotFoundForUndo(OrderId), - #[error("Ask balance for order {0}` changed for undo")] + #[error("Ask balance for order `{0}` changed for undo")] InvariantOrderAskBalanceChangedForUndo(OrderId), - #[error("Give balance for order {0}` not found for undo")] + #[error("Give balance for order `{0}` not found for undo")] InvariantOrderGiveBalanceNotFoundForUndo(OrderId), - #[error("Give balance for order {0}` changed for undo")] + #[error("Give balance for order `{0}` changed for undo")] InvariantOrderGiveBalanceChangedForUndo(OrderId), - #[error("Data for order {0}` still exist on conclude undo")] + #[error("Data for order `{0}` still exist on conclude undo")] InvariantOrderDataExistForConcludeUndo(OrderId), - #[error("Ask balance for order {0}` still exist on conclude undo")] + #[error("Ask balance for order `{0}` still exist on conclude undo")] InvariantOrderAskBalanceExistForConcludeUndo(OrderId), - #[error("Give balance for order {0}` still exist on conclude undo")] + #[error("Give balance for order `{0}` still exist on conclude undo")] InvariantOrderGiveBalanceExistForConcludeUndo(OrderId), - #[error("Ask balance for non-existing order {0}` is not zero")] + #[error("Ask balance for non-existing order `{0}` is not zero")] InvariantNonzeroAskBalanceForMissingOrder(OrderId), - #[error("Give balance for non-existing order {0}` is not zero")] + #[error("Give balance for non-existing order `{0}` is not zero")] InvariantNonzeroGiveBalanceForMissingOrder(OrderId), #[error("Coin type mismatch")] CurrencyMismatch, diff --git a/orders-accounting/src/price_calculation.rs b/orders-accounting/src/price_calculation.rs index cbd13b3d15..becfc98368 100644 --- a/orders-accounting/src/price_calculation.rs +++ b/orders-accounting/src/price_calculation.rs @@ -16,6 +16,7 @@ use common::{ chain::{output_value::OutputValue, OrderId}, primitives::Amount, + Uint256, }; use utils::ensure; @@ -59,34 +60,35 @@ pub fn calculate_fill_order( } fn calculate_filled_amount_impl(ask: Amount, give: Amount, fill: Amount) -> Option { - (give * fill.into_atoms()).and_then(|v| v / ask.into_atoms()) + let give = Uint256::from_u128(give.into_atoms()); + let fill = Uint256::from_u128(fill.into_atoms()); + let ask = Uint256::from_u128(ask.into_atoms()); + + let result = ((give * fill).expect("cannot overflow") / ask)?; + let result: u128 = result.try_into().ok()?; + + Some(Amount::from_atoms(result)) } fn ensure_currencies_and_amounts_match( order_id: OrderId, - left: &OutputValue, - right: &OutputValue, + ask: &OutputValue, + fill: &OutputValue, ) -> Result<()> { - match (left, right) { - (OutputValue::Coin(amount1), OutputValue::Coin(amount2)) => { - ensure!( - amount1 >= amount2, - Error::OrderOverbid(order_id, *amount1, *amount2) - ); + match (ask, fill) { + (OutputValue::Coin(ask), OutputValue::Coin(fill)) => { + ensure!(ask >= fill, Error::OrderOverbid(order_id, *ask, *fill)); Ok(()) } - (OutputValue::TokenV1(id1, amount1), OutputValue::TokenV1(id2, amount2)) => { - ensure!( - amount1 >= amount2, - Error::OrderOverbid(order_id, *amount1, *amount2) - ); + (OutputValue::TokenV1(id1, ask), OutputValue::TokenV1(id2, fill)) => { + ensure!(ask >= fill, Error::OrderOverbid(order_id, *ask, *fill)); ensure!(id1 == id2, Error::CurrencyMismatch); Ok(()) } (OutputValue::Coin(_), OutputValue::TokenV1(_, _)) | (OutputValue::TokenV1(_, _), OutputValue::Coin(_)) => Err(Error::CurrencyMismatch), (OutputValue::TokenV0(_), _) | (_, OutputValue::TokenV0(_)) => { - unreachable!() + Err(Error::UnsupportedTokenVersion) } } } @@ -133,7 +135,7 @@ mod tests { #[case(0, 0, 0, None)] #[case(0, 1, 1, None)] #[case(0, u128::MAX, 1, None)] - #[case(3, u128::MAX, 2, None)] + #[case(2, u128::MAX, 2, Some(u128::MAX))] #[case(1, 0, 0, Some(0))] #[case(1, 0, 1, Some(0))] #[case(1, 1, 1, Some(1))] @@ -191,6 +193,7 @@ mod tests { #[case(coin!(3), coin!(100), coin!(2), 66)] #[case(coin!(3), coin!(100), coin!(3), 100)] #[case(coin!(1), token!(u128::MAX), coin!(1), u128::MAX)] + #[case(coin!(2), token!(u128::MAX), coin!(2), u128::MAX)] fn fill_order_valid_values( #[case] ask: OutputValue, #[case] give: OutputValue, @@ -219,7 +222,6 @@ mod tests { #[case(token!(0), coin!(1), token!(1), Error::OrderOverbid(OrderId::zero(), Amount::from_atoms(0), Amount::from_atoms(1)))] #[case(coin!(1), token!(1), coin!(2), Error::OrderOverbid(OrderId::zero(), Amount::from_atoms(1), Amount::from_atoms(2)))] #[case(coin!(1), token!(u128::MAX), coin!(2), Error::OrderOverbid(OrderId::zero(), Amount::from_atoms(1), Amount::from_atoms(2)))] - #[case(coin!(2), token!(u128::MAX), coin!(2), Error::OrderOverflow(OrderId::zero()))] #[case(coin!(1), token!(1), token!(1), Error::CurrencyMismatch)] #[case(coin!(1), token!(1), token!(1), Error::CurrencyMismatch)] #[case(token!(1), token2!(1), token2!(1), Error::CurrencyMismatch)]