Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,34 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/
## [Unreleased]

### Added
- Node RPC: new method added - `chainstate_tokens_info`.
- Node RPC: new method added - `chainstate_tokens_info`, `chainstate_orders_info_by_currencies`.

- Wallet RPC:
- new methods added: `node_get_tokens_info`, `order_list_own`, `order_list_all_active`.

- Wallet CLI:
- the commands `order-create`, `order-fill`, `order-freeze`, `order-conclude` were added,
mirroring their existing RPC counterparts;
- other new commands added: `order-list-own`, `order-list-all-active`;

### Changed
- Wallet RPC:
`wallet_info`: the structure of the returned field `extra_info` was changed.
- `wallet_info`: the structure of the returned field `extra_info` was changed.
- `create_order`, `conclude_order`, `fill_order`, `freeze_order` were renamed to
`order_create`, `order_conclude`, `order_fill`, `order_freeze`.

- The format of `PartiallySignedTransaction was changed again.

- Node RPC:
- The result of `chainstate_order_info` now also indicates whether the order is frozen.

- `chainstate_pool_decommission_destination` now returns a bech32 string instead of a hexified
destination (note that in the generated documentation its result was already incorrectly
designated as "bech32 string"; now the description is correct).

- Documentation-only changes:
- Certain parameters that were designated as "string" are now designated as "bech32 string".

### Fixed
- p2p: when a peer sends a message that can't be decoded, it will now be discouraged (which is what
is normally done for misbehaving peers) and the node won't try connecting to it again.\
Expand All @@ -28,6 +48,16 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/
- Wallet CLI and RPC: the commands `account-utxos` and `standalone-multisig-utxos` and their RPC
counterparts now return correct decimal amounts for tokens with non-default number of decimals.

- Node RPC:
- `chainstate_order_info` will no longer fail if one of the order's balances became zero.

- Documentation-only changes:
- Certain parameters and/or returned values that were previously (incorrectly) designated as
"hex string" are now designated as "hexified xxx id".

- Parameters and/or returned values having the "plain" `Destination` type were incorrectly
designated as "bech32 string", while in reality they are "hexified destination".

## [1.2.0] - 2025-10-27

### Changed
Expand Down
18 changes: 18 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ atomic-traits = "0.4"
axum = "0.7"
base64 = "0.22"
bech32 = "0.11"
bigdecimal = "0.4"
bip39 = { version = "2.0", default-features = false }
bitcoin-bech32 = "0.13"
blake2 = "0.10"
Expand Down
1 change: 1 addition & 0 deletions accounting/src/delta/delta_data_collection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ impl<T: Clone> DataDelta<T> {

/// `GetDataResult` is represented by 3 states instead of typical 2 states, because it is
/// important to distinguish the case when data was explicitly deleted from the case when the data is just not there.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GetDataResult<T> {
Present(T),
Deleted,
Expand Down
6 changes: 5 additions & 1 deletion api-server/scanner-lib/src/sync/tests/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ impl OrdersAccountingView for OrderAccountingAdapterToCheckFees<'_> {
fn get_give_balance(&self, id: &OrderId) -> Result<Amount, Self::Error> {
Ok(self.chainstate.get_order_give_balance(id).unwrap().unwrap_or(Amount::ZERO))
}

fn get_all_order_ids(&self) -> Result<BTreeSet<OrderId>, Self::Error> {
Ok(self.chainstate.get_all_order_ids().unwrap())
}
}

#[rstest]
Expand Down Expand Up @@ -996,7 +1000,7 @@ async fn check_orders(
let tx = local_state.storage().transaction_ro().await.unwrap();
let scanner_data = tx.get_order(order_id).await.unwrap().unwrap();

if let Some(node_data) = tf.chainstate.get_order_info_for_rpc(order_id).unwrap() {
if let Some(node_data) = tf.chainstate.get_order_info_for_rpc(&order_id).unwrap() {
assert_eq!(scanner_data.conclude_destination, node_data.conclude_key);
assert_eq!(
scanner_data.next_nonce,
Expand Down
2 changes: 1 addition & 1 deletion chainstate/db-dumper/src/dumper_lib/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn dump_blocks_predefined() {
let chain_config =
Arc::new(chain::config::create_unit_test_config_builder().genesis_custom(genesis).build());

let block_infos = vec![
let block_infos = [
TestBlockInfo::from_input_info(TestBlockInputInfo {
height: BlockHeight::new(1),
is_mainchain: true,
Expand Down
10 changes: 3 additions & 7 deletions chainstate/launcher/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,12 @@ use chainstate::ChainstateConfig;

/// Storage type to use
#[must_use]
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum StorageBackendConfig {
#[default]
Lmdb,
InMemory,
}

impl Default for StorageBackendConfig {
fn default() -> Self {
Self::Lmdb
}
InMemory,
}

impl StorageBackendConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::BTreeMap;
use std::collections::{BTreeMap, BTreeSet};

use crate::detail::{
chainstateref::ChainstateRef,
Expand Down Expand Up @@ -570,6 +570,11 @@ impl<S: BlockchainStorageRead, V: TransactionVerificationStrategy> OrdersAccount
fn get_give_balance(&self, id: &OrderId) -> Result<Option<Amount>, Self::Error> {
self.db_tx.get_give_balance(id)
}

#[log_error]
fn get_all_order_ids(&self) -> Result<BTreeSet<OrderId>, Self::Error> {
self.db_tx.get_all_order_ids()
}
}

impl<S: BlockchainStorageWrite, V: TransactionVerificationStrategy> FlushableOrdersAccountingView
Expand Down
3 changes: 1 addition & 2 deletions chainstate/src/detail/error_classification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,8 +457,7 @@ impl BlockProcessingErrorClassification for PropertyQueryError {
// For now, since their p2p ban score is 0, let's consider them General.
PropertyQueryError::StakePoolDataNotFound(_)
| PropertyQueryError::StakerBalanceOverflow(_)
| PropertyQueryError::PoolBalanceNotFound(_)
| PropertyQueryError::OrderBalanceNotFound(_) => BlockProcessingErrorClass::General,
| PropertyQueryError::PoolBalanceNotFound(_) => BlockProcessingErrorClass::General,

PropertyQueryError::StorageError(err) => err.classify(),
PropertyQueryError::GetAncestorError(err) => err.classify(),
Expand Down
124 changes: 92 additions & 32 deletions chainstate/src/detail/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{collections::BTreeSet, num::NonZeroUsize};
use std::{
collections::{BTreeMap, BTreeSet},
num::NonZeroUsize,
};

use chainstate_storage::BlockchainStorageRead;
use chainstate_types::{BlockIndex, GenBlockIndex, Locator, PropertyQueryError};
use common::{
chain::{
block::{signed_block_header::SignedBlockHeader, BlockReward},
output_value::RpcOutputValue,
output_value::{OutputValue, RpcOutputValue},
tokens::{
NftIssuance, RPCFungibleTokenInfo, RPCIsTokenFrozen, RPCNonFungibleTokenInfo,
RPCTokenInfo, TokenAuxiliaryData, TokenId,
},
AccountType, Block, GenBlock, OrderId, RpcOrderInfo, Transaction, TxOutput,
AccountType, Block, Currency, GenBlock, OrderId, RpcOrderInfo, Transaction, TxOutput,
},
primitives::{Amount, BlockDistance, BlockHeight, Id, Idable},
};
use logging::log;
use orders_accounting::{OrderData, OrdersAccountingStorageRead};
use tokens_accounting::TokensAccountingStorageRead;
use utils::ensure;
Expand Down Expand Up @@ -436,36 +440,92 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat

pub fn get_order_info_for_rpc(
&self,
order_id: OrderId,
order_id: &OrderId,
) -> Result<Option<RpcOrderInfo>, PropertyQueryError> {
self.get_order_data(&order_id)?
.map(|order_data| {
let ask_balance = self
.get_order_ask_balance(&order_id)?
.ok_or(PropertyQueryError::OrderBalanceNotFound(order_id))?;
let give_balance = self
.get_order_give_balance(&order_id)?
.ok_or(PropertyQueryError::OrderBalanceNotFound(order_id))?;

let nonce =
self.chainstate_ref.get_account_nonce_count(AccountType::Order(order_id))?;

let initially_asked = RpcOutputValue::from_output_value(order_data.ask())
.ok_or(PropertyQueryError::UnsupportedTokenV0InOrder(order_id))?;
let initially_given = RpcOutputValue::from_output_value(order_data.give())
.ok_or(PropertyQueryError::UnsupportedTokenV0InOrder(order_id))?;

let info = RpcOrderInfo {
conclude_key: order_data.conclude_key().clone(),
initially_asked,
initially_given,
give_balance,
ask_balance,
nonce,
};

Ok(info)
})
self.get_order_data(order_id)?
.map(|order_data| self.order_data_to_rpc_info(order_id, &order_data))
.transpose()
}

pub fn get_all_order_ids(&self) -> Result<BTreeSet<OrderId>, PropertyQueryError> {
self.chainstate_ref.get_all_order_ids().map_err(PropertyQueryError::from)
}

pub fn get_orders_info_for_rpc_by_currencies(
&self,
ask_currency: Option<&Currency>,
give_currency: Option<&Currency>,
) -> Result<BTreeMap<OrderId, RpcOrderInfo>, PropertyQueryError> {
let order_ids = self.get_all_order_ids()?;

let orders_info = order_ids
.into_iter()
.map(|order_id| -> Result<_, PropertyQueryError> {
match self.get_order_data(&order_id)? {
Some(order_data) => {
let actual_ask_currency =
Self::order_currency(&order_id, order_data.ask())?;
let actual_give_currency =
Self::order_currency(&order_id, order_data.give())?;

if ask_currency
.is_none_or(|ask_currency| ask_currency == &actual_ask_currency)
&& give_currency
.is_none_or(|give_currency| give_currency == &actual_give_currency)
{
let rpc_info = self.order_data_to_rpc_info(&order_id, &order_data)?;
Ok(Some((order_id, rpc_info)))
} else {
Ok(None)
}
}
None => {
// This should never happen.
log::error!("Order data missing for existing order {order_id:x}");
Ok(None)
}
}
})
.filter_map(|res_of_opt| res_of_opt.transpose())
.collect::<Result<_, _>>()?;

Ok(orders_info)
}

fn order_currency(
order_id: &OrderId,
value: &OutputValue,
) -> Result<Currency, PropertyQueryError> {
Currency::from_output_value(value)
.ok_or(PropertyQueryError::UnsupportedTokenV0InOrder(*order_id))
}

fn order_data_to_rpc_info(
&self,
order_id: &OrderId,
order_data: &OrderData,
) -> Result<RpcOrderInfo, PropertyQueryError> {
// Note: the balances are deleted from the chainstate db once they reach zero.
let ask_balance = self.get_order_ask_balance(order_id)?.unwrap_or(Amount::ZERO);
let give_balance = self.get_order_give_balance(order_id)?.unwrap_or(Amount::ZERO);

let nonce = self.chainstate_ref.get_account_nonce_count(AccountType::Order(*order_id))?;

let initially_asked = RpcOutputValue::from_output_value(order_data.ask())
.ok_or(PropertyQueryError::UnsupportedTokenV0InOrder(*order_id))?;
let initially_given = RpcOutputValue::from_output_value(order_data.give())
.ok_or(PropertyQueryError::UnsupportedTokenV0InOrder(*order_id))?;

let info = RpcOrderInfo {
conclude_key: order_data.conclude_key().clone(),
initially_asked,
initially_given,
give_balance,
ask_balance,
nonce,
is_frozen: order_data.is_frozen(),
};

Ok(info)
}
}
14 changes: 11 additions & 3 deletions chainstate/src/interface/chainstate_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ use common::{
GenBlock,
},
tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId},
AccountNonce, AccountType, ChainConfig, DelegationId, OrderId, PoolId, RpcOrderInfo,
Transaction, TxInput, UtxoOutPoint,
AccountNonce, AccountType, ChainConfig, Currency, DelegationId, OrderId, PoolId,
RpcOrderInfo, Transaction, TxInput, UtxoOutPoint,
},
primitives::{Amount, BlockHeight, Id},
};
Expand Down Expand Up @@ -240,8 +240,16 @@ pub trait ChainstateInterface: Send + Sync {
fn get_order_give_balance(&self, id: &OrderId) -> Result<Option<Amount>, ChainstateError>;
fn get_order_info_for_rpc(
&self,
order_id: OrderId,
order_id: &OrderId,
) -> Result<Option<RpcOrderInfo>, ChainstateError>;
fn get_all_order_ids(&self) -> Result<BTreeSet<OrderId>, ChainstateError>;
/// Return infos for all orders that match the given currencies. Passing None for a currency
/// means "any currency".
fn get_orders_info_for_rpc_by_currencies(
&self,
ask_currency: Option<&Currency>,
give_currency: Option<&Currency>,
) -> Result<BTreeMap<OrderId, RpcOrderInfo>, 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`.
Expand Down
Loading