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

Add support of messages into coinsToSpend #591

Merged
merged 19 commits into from
Sep 14, 2022

Conversation

xgreenx
Copy link
Collaborator

@xgreenx xgreenx commented Sep 4, 2022

Created a new entity - Resource. It is an aggregator for the coins and messages. For more information, check the discussion at the #586

The API looks like:

struct SpendQueryElementInput {
    /// Asset ID to spend
    asset_id: AssetId,
    /// Target amount for the query
    amount: U64,
    /// The maximum number of resources for selection.
    max: Option<U64>,
}

pub struct ExcludeInput {
    /// Utxos to exclude from the selection.
    utxos: Vec<UtxoId>,
    /// Messages to exclude from the selection.
    messages: Vec<MessageId>,
}

pub enum Resource {
    Coin(Coin),
    Message(Message),
}

fn resourcesToSpend(
    // The `Address` of the resources owner.
    owner: Address, 
    // The list of requested assets` resources with asset ids, `target` amount the user wants
    // to reach, and the `max` number of resources in the selection. Several entries with the
    // same asset id are not allowed.
    query_per_asset: Vec<SpendQueryElementInput>, 
    // The excluded resources from the selection.
    exclude: Option<ExcludeInput>,
)
// The list of spendable resources per asset from the query. The length of the result is
// the same as the length of `query_per_asset`. The ordering of assets and `query_per_asset`
// is the same.
-> Result<Vec<Vec<Resource>>>

Added some abstraction to remove logic duplicating. I want to reuse it in the other schemas like balance.
Added schema::Scalar for MessageId.
Added message_id for the schema::Message.

Closes #586

…and messages.

We can't use `Input` or `Output` naming because we already do that with transactions.
So I introduced a new one. We can think about the naming in the PR=)

Renamed `coinsToSpent` into `banknotesToSpent`. Move it to a separate file.
Changed the signature. Not it returns `Vec<Vec<Banknote>>` instead of `Vec<Coin>`.
The result is a list of `Banknote` per asset(we can have many different assets, so it is why it has an additional vector).
Replaced exclude vector with two vectors in the separate struct. One vector allows filtering by utxos, another by messages.

Reduced the number of operations in the `banknotesToSpent`.
Added some abstraction to remove logic duplicating.
I want to reuse it in the other schemas like `balance`.

Added `schema::Scalar` for `MessageId`.

I need to add the support of `Message` into tests and the code. I will do it in the next commit=)
@ControlCplusControlV
Copy link
Contributor

I see a lot of API changes in here, are you sure of how this affects downstream consumers of fuel-core yet?

@xgreenx
Copy link
Collaborator Author

xgreenx commented Sep 4, 2022

Do you ask about coinsToSpend method signature or changes also in the database?

coinsToSpend is an inevitable change because we want to work with different objects(coins, messages). Because it breaks API, we can make more changes to make the API clear. For example, return banknotes per asset, and pass max_input per asset.

The changes in the database it forcing people to change how they work with it to have more optimal code.

@xgreenx xgreenx self-assigned this Sep 4, 2022
@xgreenx xgreenx requested a review from Voxelot September 4, 2022 10:01
@ControlCplusControlV
Copy link
Contributor

ControlCplusControlV commented Sep 4, 2022

Do you ask about coinsToSpend method signature or changes also in the database?

coinsToSpend is an inevitable change because we want to work with different objects(coins, messages). Because it breaks API, we can make more changes to make the API clear. For example, return banknotes per asset, and pass max_input per asset.

Changing to coinsToSpend I agree is inevitable for the best, I am more referring to the changes on both endpoint name and in the GQL schema. Any breaking issues there should be identified and a fix prepped there before merging this though to avoid issue

@xgreenx
Copy link
Collaborator Author

xgreenx commented Sep 5, 2022

Before I will update the tests let's discuss the final query signature in the issue=)

@xgreenx xgreenx changed the title Add support of messages into coinsToSpent Add support of messages into coinsToSpend Sep 6, 2022
# Conflicts:
#	fuel-client/src/client.rs
#	fuel-core/src/coin_query.rs
#	fuel-core/src/database/coin.rs
#	fuel-core/src/schema/coin.rs
#	fuel-tests/tests/coin.rs
Uncommented and fixed old tests.
@xgreenx xgreenx added breaking A breaking api change graphql-api Affects API of the GraphQL labels Sep 12, 2022
…m to work with coins, messages, and coins and messages test cases.

Added new field to `schema::Message` - `message_id`. It is calculated based on the `Message`.
Added check of errors in the client's tests.
@xgreenx xgreenx marked this pull request as ready for review September 12, 2022 23:16
@@ -0,0 +1,218 @@
use crate::{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to avoid a generic utils module, as it makes organization less clear and will just keep growing with potentially unrelated things.

Since this is mostly about coins and messages, maybe rename this utils module into resources within the database instead?

let mut collected_amount = 0u64;
let mut resources = vec![];

for coin in inputs {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for coin in inputs {
for resource in inputs {


/// At least required `target` of the query per asset's `id` with `max` resources.
#[derive(Clone)]
pub struct Asset {
Copy link
Member

@Voxelot Voxelot Sep 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a selection criteria, not an asset 🤔

Maybe something like AssetSpendTarget?

/// The primary type of spent or not spent resources(coins, messages, etc). The not spent resource
/// can be used as a source of information for the creation of the transaction's input.
#[derive(Debug)]
pub enum Resource<C, M> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the schema has its own analog to resource, why include these generics here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid unnecessary Copy and Clone. The query to DB returns Resource<Cow<Coin>, Cow<Message>>. But the graphql query returns Resource<Coin, Message>.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the schema has its own analog to resource

It looks like the schema doesn't rely on this enum, since it has it's own: https://github.com/FuelLabs/fuel-core/blob/feature/coins-to-spent-messages/fuel-core/src/schema/resource.rs#L52

Couldn't we just simplify this type by making it always a Cow?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because Cow requires lifetime from DB, and in the resources_to_spend we can't return Vec<Vec<Resource<Cow, Cow>>>=(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need Cow? Or is it always owned once deserialized from the DB anyways? Even though the storage trait uses Cow, I don't think it would ever be borrowed unless we had some kind of zero-copy deserialization going on.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if we use RocksDB, in this case, it is useless=) I tried to be consistent with our API. Database returns Cow, and in some cases (maybe in the future), it can be useful.

If you think it adds unjustified complexity, I can remove it=)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh it's not a big deal. I just prefer to find ways to hide generics when possible since it can get out of hand sometimes (like in substrate codebases lol)

@@ -221,6 +221,8 @@ impl KeyValueStore for RocksDb {
.iterator_cf_opt(&self.cf(column), opts, iter_mode)
.map(|item| {
item.map(|(key, value)| {
// TODO: Avoid copy of the slices into vector.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also adjust the API to make use of boxed slices instead of vec 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, the question do we want to add complexity to database API=) But it will force us to work with raw slice where it is possible=)

.unwrap();
assert_eq!(resources_per_asset.len(), 2);
assert_eq!(resources_per_asset[0].len(), 3);
assert_eq!(resources_per_asset[1].len(), 3);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also check that the returned resources add up to >= target in these tests?

@Voxelot
Copy link
Member

Voxelot commented Sep 13, 2022

Done reviewing for now, will wait for the requested changes before approving.

Voxelot
Voxelot previously approved these changes Sep 13, 2022
@@ -76,8 +76,9 @@ impl BalanceQuery {
) -> async_graphql::Result<Balance> {
let db = ctx.data_unchecked::<Database>();

// TODO: Reuse [`AssetQuery`](crate::database::utils::AssetQuery) with messages
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: would be nice to see Issues created for these TODOs (if not already)

leviathanbeak
leviathanbeak previously approved these changes Sep 14, 2022
Copy link
Contributor

@leviathanbeak leviathanbeak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, there are handful of TODOs, would be nice that Issues are created for and possibly referenced

Copy link
Contributor

@ControlCplusControlV ControlCplusControlV left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, will move new coinsToSpend tests over to my rstest PR once merged. We may also want to look at organizing fuel-tests in a future issue as it is mostly flat right now and could benefit from tests being organized into folders

Also +1 on moving TODOs to issues (with TODO comment referencing the issue #)

Renamed `CoinQueryError` into `ResourceQueryError`.
Added `asset_id` and `collected_amount` into `ResourceQueryError`.
Voxelot
Voxelot previously approved these changes Sep 14, 2022
# Conflicts:
#	fuel-core/src/coin_query.rs
#	fuel-core/src/database.rs
#	fuel-core/src/database/coin.rs
#	fuel-core/src/database/message.rs
#	fuel-core/src/state.rs
#	fuel-core/src/test_utils.rs
@xgreenx xgreenx merged commit bd5901f into master Sep 14, 2022
@xgreenx xgreenx deleted the feature/coins-to-spent-messages branch September 14, 2022 23:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking A breaking api change graphql-api Affects API of the GraphQL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Include messages in coinsToSpend API
4 participants