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

[core]: add computed read for components #714

Closed
1 of 5 tasks
ponderingdemocritus opened this issue Aug 5, 2023 · 10 comments
Closed
1 of 5 tasks

[core]: add computed read for components #714

ponderingdemocritus opened this issue Aug 5, 2023 · 10 comments
Assignees
Labels
dojo-core cairo core tasks enhancement New feature or request
Milestone

Comments

@ponderingdemocritus
Copy link
Contributor

ponderingdemocritus commented Aug 5, 2023

This has been discussed before but adding it for posterity and in case a new contributor wants to tackle it.

Is your feature request related to a problem? Please describe.
Games require computed values for context, currently, it is not possible to query for computed values within components.

Describe the solution you'd like
The ability to add an attribute to a component implementation that allows clients to view the computed value.

Indexing writer contract view functions

from: @shramee

A contract function that doesn't update storage used to be called a view function on StarkNet. I'm not sure if that's still the case but below, by view I mean contract view function.

Experience for the developers remains the same, but indexers can provide config to index certain views

Torii to support indexing for any view function on any of the writer contracts.

  • Support call view functions in deployed contracts #1123
    • Takes the writer contract name/address.
    • Calldata as a vec of felts
    • Returns a vec of felts
  • Torii: Index call view functions based on config
  • Torii: GraphQL call to view functions
  • Torii: GRPC call to view functions
  • Torii: Link view functions to models if the view takes single Model argument
    • Reasoning and thoughts:
    • If the view takes a single parameter of type Model, Torii can provide it all from it's database.
    • The view cached values will be refreshed when the model updates.
    • With semantics, we have an idea of the models being accessed. Getting the key of the read model can be challenging if the models don't share a common key structure.
    • For reliability and atomicity of transactions, it'd be more robust to just take the whole models as input.
    • Torii can then link the view functions to relevant models and provide config options to link the view as a "computed" value on model data.
    • Examples:
    • The function below can be linked to Army model.
    fn compute_population(self: @ContractState, army: Army) -> u16;

WIP Multiple models in one function.

Challenge: When to reload cache?
Another Case: When a model determines which other entity should be fetched to get the data for compute.
  • This can be linked to Army model too. The keys either need to share the same structure, or second key should be derived from something on army.
    fn compute_wages(self: @ContractState, army: Army, wages: Wages) -> u16;
  • Same key structure as in for example both take contract_address and game_id for key.
    Torii will allow you to link the view function taking in a model to be linked to Model data as a property.
@ponderingdemocritus ponderingdemocritus added enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed dojo-core cairo core tasks labels Aug 5, 2023
@dubzn
Copy link
Contributor

dubzn commented Aug 17, 2023

Hi sir! Is someone working on this? I would like to give it a try.

@tarrencev
Copy link
Contributor

Hey! You're welcome to give it a shot!

@ponderingdemocritus ponderingdemocritus moved this to 📋 Backlog in Dojo Aug 22, 2023
@ponderingdemocritus ponderingdemocritus moved this from 📋 Backlog to 🏗 In progress in Dojo Aug 22, 2023
@ponderingdemocritus
Copy link
Contributor Author

Hey @dubzn have you made progress on this?

@tarrencev tarrencev assigned shramee and unassigned dubzn Oct 12, 2023
@tarrencev tarrencev moved this from 🏗 In progress to 🆕 Prioritized in Dojo Oct 12, 2023
@tarrencev tarrencev removed good first issue Good for newcomers help wanted Extra attention is needed labels Oct 12, 2023
@tarrencev tarrencev added this to the 0.4.0 milestone Oct 12, 2023
@shramee
Copy link
Contributor

shramee commented Oct 27, 2023

Implementation:

Declaration

  1. New computed attribute for model struct fields. ⚠️ The fields should appear at the end of the model struct.
#[derive(Model, Copy, Drop, Serde)]
struct Army {
    #[key]
    player: ContractAddress,
    archers: u16,
    cavalry: u16,
    infantry: u16,
    // Computed fields should appear at the end
    #[computed]
    population: u16,
}
  1. We expect an impl containing fn compute_population(self: @Army) -> u16,

    • snapshot of army
    • returns the same type
  2. ⚠️ All computed field types should implement Default. This is done for all primitives.

Introspection

Introspect provides serialised Defaults for computed values.

fn serialize_computed_defaults( ref values ) {
    layout.append( Default::<u16>::default() );
}

Reconstruction

After fetch we append the default values before deserialising (similar to how we do for keys).

SchemaIntrospection::<Army>::serialize_computed_defaults( ref __Army_values__ );
...
let mut army = serde::Serde::<Army>::deserialize( ref __Army_values_span__ );

Finally, we add the computed values,

army.population = army.compute_population();

All done!

@shramee
Copy link
Contributor

shramee commented Oct 27, 2023

@ponderingdemocritus @tarrencev ^^

@tarrencev
Copy link
Contributor

tarrencev commented Oct 27, 2023

@shramee a computed value doesn't need to exist on the struct. it should be defined based on the #[computed] macro on a model's impl. The value will exist as part of the offchain data model. So introspection and reconstruction are not necessary. If the developer wants the computed value at runtime, they can call the method.

For a computed value, we want to be able to understand it's dependencies during compilation, so we can identity the underlying values that should trigger recompilation. For example:

struct Vec2 {
   x: u32,
   y: u32,
}

#[computed]
fn abs(self: @Vec2) -> Vec2 {
    return Vec2 { x: a.x.abs(), y: a.y.abs() };
}

@shramee
Copy link
Contributor

shramee commented Oct 27, 2023

So we have,

  • Lookup functions with computed attr
  • Should take only one arg of type self: @Model
  • Pass 1: Index 'Model' > [FnSyntaxNode]
  • Pass 2: Insert the function syntax in the component class
  • Add the entrypoint details to manifest
  • Have Torii access these (or maybe via an executer to run it from the class)
  • Save it along with models data in Torii?
  • Expose it from Torii as a value on model
  • Recompute when the model changes

@shramee
Copy link
Contributor

shramee commented Oct 31, 2023

@ponderingdemocritus @tarrencev @kariy,
Guys updated the description...

@shramee
Copy link
Contributor

shramee commented Nov 3, 2023

New usage:

  1. #[computed]

    • Adds table in Torii DB for queryable lookup

    • Columns for parameters

    • For example:

       #[computed]
       tile_terrain(self: @ContractState, vec: Vec2) -> felt252;

      Adds table a little like this ComputedValue_Quadrant

      x y Terrain
      1 1 land
      1 2 land
      2 1 water
  2. #[computed(ModelName)]

    • Should add a column in model table.
    • For example:
       #[computed(Position)]
       fn quadrant(self: @ContractState, pos: Position) -> Quadrant;
      Adds column quadrant on Position table.

Examples:

#[external(v0)]
#[computed]
fn tile_terrain(self: @ContractState, vec: Vec2) -> felt252 {
    'land'
}

#[external(v0)]
#[computed(Position)]
fn quadrant(self: @ContractState, pos: Position) -> Quadrant {
    // 10 is zero
    if pos.vec.x < 10 {
        if pos.vec.y < 10 {
            Quadrant::NN
        } else {
            Quadrant::NP
        }
    } else {
        if pos.vec.y < 10 {
            Quadrant::PN
        } else {
            Quadrant::PP
        }
    }
}

@glihm
Copy link
Collaborator

glihm commented Feb 14, 2024

Will consider it as closed by #1152. Do no hesitate to re-open is required.

@glihm glihm closed this as completed Feb 14, 2024
@github-project-automation github-project-automation bot moved this from 🆕 Prioritized to ✅ Done in Dojo Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dojo-core cairo core tasks enhancement New feature or request
Projects
No open projects
Status: Done
Development

No branches or pull requests

5 participants