Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support orders in the wallet #1823

Merged
merged 12 commits into from
Oct 14, 2024
Merged
1 change: 1 addition & 0 deletions Cargo.lock

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

Original file line number Diff line number Diff line change
Expand Up @@ -327,20 +327,21 @@ impl ConstrainedValueAccumulator {

Ok((CoinOrTokenId::Coin, Amount::ZERO))
}
AccountCommand::FillOrder(id, fill_amount, _) => {
AccountCommand::FillOrder(id, fill_amount_in_ask_currency, _) => {
let order_data = orders_accounting_delta
.get_order_data(id)
.map_err(|_| orders_accounting::Error::ViewFail)?
.ok_or(orders_accounting::Error::OrderDataNotFound(*id))?;
let filled_amount = orders_accounting::calculate_fill_order(
&orders_accounting_delta,
*id,
*fill_amount,
*fill_amount_in_ask_currency,
)?;

{
// Ensure that spending won't result in negative balance
let _ = orders_accounting_delta.fill_order(*id, *fill_amount)?;
let _ =
orders_accounting_delta.fill_order(*id, *fill_amount_in_ask_currency)?;
let _ = orders_accounting_delta.get_ask_balance(id)?;
let _ = orders_accounting_delta.get_give_balance(id)?;
}
Expand All @@ -352,7 +353,7 @@ impl ConstrainedValueAccumulator {
let ask_currency = CoinOrTokenId::from_output_value(order_data.ask())
.ok_or(Error::UnsupportedTokenVersion)?;

Ok((ask_currency, *fill_amount))
Ok((ask_currency, *fill_amount_in_ask_currency))
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions chainstate/src/detail/error_classification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,8 @@ impl BlockProcessingErrorClassification for PropertyQueryError {
| PropertyQueryError::BlockForHeightNotFound(_)
| PropertyQueryError::GenesisHeaderRequested
| PropertyQueryError::InvalidStartingBlockHeightForMainchainBlocks(_)
| PropertyQueryError::InvalidBlockHeightRange { .. } => {
| PropertyQueryError::InvalidBlockHeightRange { .. }
| PropertyQueryError::UnsupportedTokenV0InOrder(_) => {
BlockProcessingErrorClass::General
}
// Note: these errors are strange - sometimes they don't look like General, judging
Expand All @@ -435,7 +436,8 @@ impl BlockProcessingErrorClassification for PropertyQueryError {
// For now, since their p2p ban score is 0, let's consider them General.
PropertyQueryError::StakePoolDataNotFound(_)
| PropertyQueryError::StakerBalanceOverflow(_)
| PropertyQueryError::PoolBalanceNotFound(_) => BlockProcessingErrorClass::General,
| PropertyQueryError::PoolBalanceNotFound(_)
| PropertyQueryError::OrderBalanceNotFound(_) => BlockProcessingErrorClass::General,

PropertyQueryError::StorageError(err) => err.classify(),
PropertyQueryError::GetAncestorError(err) => err.classify(),
Expand Down
38 changes: 37 additions & 1 deletion chainstate/src/detail/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ use chainstate_types::{BlockIndex, GenBlockIndex, Locator, PropertyQueryError};
use common::{
chain::{
block::{signed_block_header::SignedBlockHeader, BlockReward},
output_value::RpcOutputValue,
tokens::{
NftIssuance, RPCFungibleTokenInfo, RPCIsTokenFrozen, RPCNonFungibleTokenInfo,
RPCTokenInfo, TokenAuxiliaryData, TokenId,
},
Block, GenBlock, OrderData, OrderId, Transaction, TxOutput,
AccountType, Block, GenBlock, OrderData, OrderId, RpcOrderInfo, Transaction, TxOutput,
},
primitives::{Amount, BlockDistance, BlockHeight, Id, Idable},
};
Expand Down Expand Up @@ -421,4 +422,39 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
) -> Result<Option<Amount>, PropertyQueryError> {
self.chainstate_ref.get_give_balance(id).map_err(PropertyQueryError::from)
}

pub fn get_order_info_for_rpc(
&self,
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)
})
.transpose()
}
}
6 changes: 5 additions & 1 deletion chainstate/src/interface/chainstate_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use common::{
},
tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId},
AccountNonce, AccountType, ChainConfig, DelegationId, OrderData, OrderId, PoolId,
Transaction, TxInput, UtxoOutPoint,
RpcOrderInfo, Transaction, TxInput, UtxoOutPoint,
},
primitives::{Amount, BlockHeight, Id},
};
Expand Down Expand Up @@ -223,6 +223,10 @@ pub trait ChainstateInterface: Send + Sync {
fn get_order_data(&self, id: &OrderId) -> Result<Option<OrderData>, ChainstateError>;
fn get_order_ask_balance(&self, id: &OrderId) -> Result<Option<Amount>, ChainstateError>;
fn get_order_give_balance(&self, id: &OrderId) -> Result<Option<Amount>, ChainstateError>;
fn get_order_info_for_rpc(
&self,
order_id: OrderId,
) -> Result<Option<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
13 changes: 11 additions & 2 deletions chainstate/src/interface/chainstate_interface_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ use common::{
block::{signed_block_header::SignedBlockHeader, Block, BlockReward, GenBlock},
config::ChainConfig,
tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId},
AccountNonce, AccountType, DelegationId, OrderData, OrderId, PoolId, Transaction, TxInput,
TxOutput, UtxoOutPoint,
AccountNonce, AccountType, DelegationId, OrderData, OrderId, PoolId, RpcOrderInfo,
Transaction, TxInput, TxOutput, UtxoOutPoint,
},
primitives::{id::WithId, Amount, BlockHeight, Id, Idable},
};
Expand Down Expand Up @@ -790,6 +790,15 @@ where
.get_order_give_balance(id)
.map_err(ChainstateError::from)
}

#[tracing::instrument(skip_all, fields(id = %id))]
fn get_order_info_for_rpc(&self, id: OrderId) -> Result<Option<RpcOrderInfo>, ChainstateError> {
self.chainstate
.query()
.map_err(ChainstateError::from)?
.get_order_info_for_rpc(id)
.map_err(ChainstateError::from)
}
}

// TODO: remove this function. The value of an output cannot be generalized and exposed from ChainstateInterface in such way
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use common::{
config::ChainConfig,
tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId},
AccountNonce, AccountType, Block, DelegationId, GenBlock, OrderData, OrderId, PoolId,
Transaction, TxInput, UtxoOutPoint,
RpcOrderInfo, Transaction, TxInput, UtxoOutPoint,
},
primitives::{Amount, BlockHeight, Id},
};
Expand Down Expand Up @@ -421,6 +421,13 @@ where
fn get_order_give_balance(&self, id: &OrderId) -> Result<Option<Amount>, ChainstateError> {
self.deref().get_order_give_balance(id)
}

fn get_order_info_for_rpc(
&self,
order_id: OrderId,
) -> Result<Option<RpcOrderInfo>, ChainstateError> {
self.deref().get_order_info_for_rpc(order_id)
}
}

#[cfg(test)]
Expand Down
25 changes: 23 additions & 2 deletions chainstate/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ use common::{
address::{dehexify::to_dehexified_json, Address},
chain::{
tokens::{RPCTokenInfo, TokenId},
ChainConfig, DelegationId, PoolId, TxOutput,
ChainConfig, DelegationId, OrderId, PoolId, RpcOrderInfo, TxOutput,
},
primitives::{Amount, BlockHeight, Id},
};
use rpc::{subscription, RpcResult};
use serialization::hex_encoded::HexEncoded;
pub use types::{
input::RpcUtxoOutpoint, output::RpcTxOutput, signed_transaction::RpcSignedTransaction,
input::RpcUtxoOutpoint,
output::{RpcOutputValueIn, RpcOutputValueOut, RpcTxOutput},
signed_transaction::RpcSignedTransaction,
};

#[rpc::describe]
Expand Down Expand Up @@ -155,6 +157,10 @@ trait ChainstateRpc {
#[method(name = "token_info")]
async fn token_info(&self, token_id: String) -> RpcResult<Option<RPCTokenInfo>>;

/// Get order information, given an order id, in address form.
#[method(name = "order_info")]
async fn order_info(&self, order_id: String) -> RpcResult<Option<RpcOrderInfo>>;

/// Exports a "bootstrap file", which contains all blocks
#[method(name = "export_bootstrap_file")]
async fn export_bootstrap_file(
Expand Down Expand Up @@ -376,6 +382,21 @@ impl ChainstateRpcServer for super::ChainstateHandle {
)
}

async fn order_info(&self, order_id: String) -> RpcResult<Option<RpcOrderInfo>> {
rpc::handle_result(
self.call(move |this| {
let chain_config = this.get_chain_config();
let result: Result<Option<RpcOrderInfo>, _> =
dynamize_err(Address::<OrderId>::from_string(chain_config, order_id))
.map(|address| address.into_object())
.and_then(|order_id| dynamize_err(this.get_order_info_for_rpc(order_id)));

result
})
.await,
)
}

async fn export_bootstrap_file(
&self,
file_path: &std::path::Path,
Expand Down
46 changes: 29 additions & 17 deletions chainstate/src/rpc/types/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,28 @@ use common::{
timelock::OutputTimeLock, tokens::TokenId, ChainConfig, DelegationId, Destination, PoolId,
TxOutput,
},
primitives::amount::RpcAmountOut,
primitives::amount::{RpcAmountIn, RpcAmountOut},
};
use crypto::vrf::VRFPublicKey;
use rpc::types::RpcHexString;

use super::token::{RpcNftIssuance, RpcTokenIssuance};

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, rpc_description::HasValueHint)]
#[serde(tag = "type", content = "content")]
pub enum RpcOutputValueIn {
Coin {
amount: RpcAmountIn,
},
Token {
id: RpcAddress<TokenId>,
amount: RpcAmountIn,
},
}

#[derive(Debug, Clone, serde::Serialize, rpc_description::HasValueHint)]
#[serde(tag = "type", content = "content")]
pub enum RpcOutputValue {
pub enum RpcOutputValueOut {
Coin {
amount: RpcAmountOut,
},
Expand All @@ -39,14 +51,14 @@ pub enum RpcOutputValue {
},
}

impl RpcOutputValue {
impl RpcOutputValueOut {
pub fn new(chain_config: &ChainConfig, value: OutputValue) -> Result<Self, AddressError> {
let result = match value {
OutputValue::Coin(amount) => RpcOutputValue::Coin {
OutputValue::Coin(amount) => RpcOutputValueOut::Coin {
amount: RpcAmountOut::from_amount(amount, chain_config.coin_decimals()),
},
OutputValue::TokenV0(_) => unimplemented!(),
OutputValue::TokenV1(token_id, amount) => RpcOutputValue::Token {
OutputValue::TokenV1(token_id, amount) => RpcOutputValueOut::Token {
id: RpcAddress::new(chain_config, token_id)?,
amount: RpcAmountOut::from_amount(amount, chain_config.coin_decimals()),
},
Expand Down Expand Up @@ -109,16 +121,16 @@ impl RpcHashedTimelockContract {
#[serde(tag = "type", content = "content")]
pub enum RpcTxOutput {
Transfer {
value: RpcOutputValue,
value: RpcOutputValueOut,
destination: RpcAddress<Destination>,
},
LockThenTransfer {
value: RpcOutputValue,
value: RpcOutputValueOut,
destination: RpcAddress<Destination>,
timelock: OutputTimeLock,
},
Burn {
value: RpcOutputValue,
value: RpcOutputValueOut,
},
CreateStakePool {
pool_id: RpcAddress<PoolId>,
Expand Down Expand Up @@ -148,36 +160,36 @@ pub enum RpcTxOutput {
data: RpcHexString,
},
Htlc {
value: RpcOutputValue,
value: RpcOutputValueOut,
htlc: RpcHashedTimelockContract,
},
AnyoneCanTake {
authority: RpcAddress<Destination>,
ask_value: RpcOutputValue,
give_value: RpcOutputValue,
ask_value: RpcOutputValueOut,
give_value: RpcOutputValueOut,
},
}

impl RpcTxOutput {
pub fn new(chain_config: &ChainConfig, output: TxOutput) -> Result<Self, AddressError> {
let result = match output {
TxOutput::Transfer(value, destination) => RpcTxOutput::Transfer {
value: RpcOutputValue::new(chain_config, value)?,
value: RpcOutputValueOut::new(chain_config, value)?,
destination: RpcAddress::new(chain_config, destination)?,
},
TxOutput::LockThenTransfer(value, destination, timelock) => {
RpcTxOutput::LockThenTransfer {
value: RpcOutputValue::new(chain_config, value)?,
value: RpcOutputValueOut::new(chain_config, value)?,
destination: RpcAddress::new(chain_config, destination)?,
timelock,
}
}
TxOutput::Htlc(value, htlc) => RpcTxOutput::Htlc {
value: RpcOutputValue::new(chain_config, value)?,
value: RpcOutputValueOut::new(chain_config, value)?,
htlc: RpcHashedTimelockContract::new(chain_config, &htlc)?,
},
TxOutput::Burn(value) => RpcTxOutput::Burn {
value: RpcOutputValue::new(chain_config, value)?,
value: RpcOutputValueOut::new(chain_config, value)?,
},
TxOutput::CreateStakePool(id, data) => RpcTxOutput::CreateStakePool {
pool_id: RpcAddress::new(chain_config, id)?,
Expand Down Expand Up @@ -210,8 +222,8 @@ impl RpcTxOutput {
},
TxOutput::AnyoneCanTake(data) => RpcTxOutput::AnyoneCanTake {
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())?,
ask_value: RpcOutputValueOut::new(chain_config, data.ask().clone())?,
give_value: RpcOutputValueOut::new(chain_config, data.give().clone())?,
},
};
Ok(result)
Expand Down
Loading
Loading