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

Effect Network contracts V2 #117

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open

Effect Network contracts V2 #117

wants to merge 46 commits into from

Conversation

jeisses
Copy link
Member

@jeisses jeisses commented Aug 3, 2023

Summary

Effect Network V2 is an improved implementation of the network smart contracts. At its core is a new reservation and qualication system that is more performant and makes it easier for client integrations.

This version requires a new deployment of the force.efx contract. We will provide an automatic migration of all qualifications. Campaign data will be accessible as a backup but not be migrated to the new contract.

Features

The following main features will be enabled by this update:

Instant reservations
The highlight of this update: the smart contract is now in charge of assigning a worker to a task. Previously, the logic for selecting a task to work on was embedded in the SDK. The SDK would communicate to the smart contract which task it wants to work on. This was the source of many errors and performance issues on the platform. It also puts a lot of complexity on the implementation of APIs.

In the new system, a client implementation just has to call reservetask on a campaign, and the smart contract will efficiently dispatch a suitable task to the worker. There is no chance for race conditions.

Infinite repetitions
There is no longer a limit on the amount of repetitions a batch can have (this used to be a round 50). This enables high-repetition tasks like questionnaire and play testing, and enables us to use qualififiers that never expire.

Better campaign indexing
The smart contracts have improved data and indices for fetching active campaigns. Clients no longer have to fetch all submission data to determine which tasks they can work on (because of automated reservations). This removes most loading times and puts less strain on our RPC nodes.

Task expiration per campaign
Campaigns can now specify after how long reservations expire, this used to be global.

NFT qualifications
Qualification have been swapped with AtomicAsset NFTs. Campaigns can filter on specific assets, collections, or templates. Additionally campaigns can filter on on-chain NFT attributes, which enables an extremely dynamic and portable qualification system.

Effect Network will provide a standard collection of NFTs, mirrored from the current qualification set, that embed expected qualification properties (like being soulbound and having a revokable attribute). These will be airdropped to the mirror the qualification table.

Task streaming*
With the new reservation system, clients will be able to reserve tasks ahead in a campaign that is currently empty. They will be able to receive a push notification as soon the campaign receives more tasks and their reservation becomes active. This eliminates "task wars" (race conditions when users try to reserve much wanted tasks). It is also a requirement for allowing AI-agents on the platform.

Skip tasks*
The new reservation system allows for workers to skip tasks they do not like, this is a much requested feature that was not possible in the previous system.

Implementation

This section explains how to implement a client for the new structure:

Tables

  • [new] reservation: contains active reservations
  • submission: contains results after user submitted task
  • [new] acctaskidx: track which task index a worker is at per campaign
  • [new] repsdone: track how many repetitions are completed for each task index in a campaign (only after tasks_done)
  • campaign, batch, payment: unchanged

Reservation flow for clients

Find campaigns to work on

  • Get all AtomicAssets of the user
  • Get all rows from the campaigns table
  • Filter on active ones: campaign.total_tasks > campaign.tasks_done

Resolve qualifications

  • Check each entry in the campaign.qualis vector. It has the following structure:
{"type": 0, "address": ["uint32", 123], "data_filter": null}

type 0 = AtomicAsset collection name (eosio::name)
type 1 = AtomicAsset template name (eosio::name)
type 2 = AtomicAsset asset ID (uint32)

address = a variant to filter on. specify as ["type", value] where type is "name" or "uint32" (see types above)
data_filter = filters for NFT attributes, can be left `null` for now
  • Search through the users AtomicAssets and filter create a list of the Asset IDs (integers) that match the Quali. This array of Asset IDs must align with the qualification array
  • Filter the campaign list on the ones that the user qualifies for

Find active reservations

  • To find all users reservation: filter the reservation table by account_id (index = 3)
  • To find the user reservation in a campaign: filter on acccamp (index 1) with composite index (uint64_t{account_id.value()} << 32) | campaign_id

Reserve a task
To work on a task in a campaign:

  • Fetch the last task index the user did from the acctaskidx table. This can be directly fetched using the composite primary key: (uint64_t{account_id} << 32) | campaign_id;
  • Call reservetask(campaign_id, account_id, quali_assets, payer, sig)
    • quali_assets: can be null, then the smart contract will search through all the assets of the user, which consumes more CPU
    • sig (for BSC users only): to avoid replay attacks, the signature is composed of (mark)(last_task_done)(campaign_id). The mark value is 6
  • Wait for the transaction to process, find the reserved task by polling as described in find active reservations

Fetch the task data

  • Load the batch the task is in (get _task_.batch_id from the batch table)
  • Get the batch IPFS hash from batch.content.value
  • Load the IPFS object and confirm it is a JSON array. Get the _task_.task_idxth item from the array
  • Render the campaign template with that task data

Submit task

  • Call submittask(camapign_id, task_idx, data, account_id, sig). Note to use _task_.task_idx for the task_idx parameter (not the ID).
    • sig (for BSC only): to avoid replay attacks, the signature is (mark)(campaign_id)(task_idx)(data). The mark value is 5.

Reserve next task
The same process as above, but make sure to update last_task_done for BSC users

- ABIs changed the field names of maps from "key" and "value" to "first"
and "second".
- `eosio-cpp` has been renamed to `cdt-cpp`
- abigen is part of `cdt-cpp` and no longer a seperate build step
- Note: there appears to be a bug in the ABI generation when using
version 4.0.0, so we stick with 3.1.0 for now
it used to be on a global config level
it serves as a nonce for transactions with a raw signature, but there
is not need to have it as an action paramter
@djmbritt
Copy link
Member

djmbritt commented Aug 8, 2023

Review Effect Network Contract v2

Some review points for the implementation details for Effect Network Smart Contract Spec

Instant Reservations

I think it's useful to be explicit about how this will speed up the frontend.
The frontend won't need to do all the work of downloading data from the blockchain to do the checks.

Infinite Reps

Side note, What will be the new limit for how large a batch can be?
Are we still maintaining the campaign, batch, task structure?

Better campaign indexing

The frontend comment mentioned in [Instant Reservations](#Instant Resevations ✅) could also apply here.

Task expiration per campaign

I can't remember if we ever had an issue with this, I would classify this as lower priority compared to the other features in this list and the other key elements I mention written down below.

NFT Qualis

This should help make a user profile more valuable and should help mitigate users who create throw away accounts.

Task Streaming

Could you expand a little bit on how this could help with allowing-AI agents?

Skip tasks

Agreed that this is a needed feature. Especially with larger datasets, where users try to scam the task, instead of skipping it when it gets too dificult.

Other notes

There are some key elements that, I think, are still missing:

Custom payout times

The current payout time is too short. Especially if we want to be able to validate tasks. We need a mechanism to adjust the payout time per batch

Price per batch instead of price per campaign

We have had issues before where we adjusted the price of a campaign, and it ended up affecting operations, Where users were annoyed with the payout because it did not conform to their expectations.

Task rejection as Requestor

We need a mechanism to actually reject a task and reclaim the tokens that are in escrow for the user.
There is no mechanism currently to do this, and I think it will help the quality of the submissions if users know their funds are reliant on high-quality submissions.

@jeisses
Copy link
Member Author

jeisses commented Aug 9, 2023

I think it's useful to be explicit about how this will speed up the frontend.

Depends on the SDK / frontend implementation. As finding possible reservations was extremely slow, I'm satisfied with the observation that this problem is now eliminated.

Side note, What will be the new limit for how large a batch can be?
Are we still maintaining the campaign, batch, task structure?

Good point. Technically there is no upper-limit anymore, but we probably want to set one as batch-removal is the only way to clear RAM for users or relayers. It is also possible to go with a time-based limit

[task expiration] ... I would classify this as lower priority compared to the other features in this list and the other key elements

It is already part of the implementation in the PR

Task Streaming, Could you expand a little bit on how this could help with allowing-AI agents?

AI-agents reverse the platform from a task-first queue to a worker-first queue. In the new system we now longer have "task-wars" where multiple participants (users or AI) are trying to claim the same task. The proposed method is to allow making reservations in advance, before tasks are even created yet. This needs a full write-up, but hope that you get the gist of it

Custom [per batch] payout times

This should not be hard to implement, I see if it can be included in this PR. This would be a payout time per campaign, not batch (see my comment below).
Out of interest, could you elaborate a bit on why they are too low now?

Price per batch instead of price per campaign

In the new model, batches are abstracted away (user can not decide in which batch they will work). So we are trying to keep batches more uniform; they are primarily meant to append data to an existing campaign.
If you want custom payout times, task expiration, or prices, you will have to create more campaigns.

BTW, in V1 price per batch is already a smart contract feature, as in, if you change the campaign price, existing batches are not affected. The annoyance you mention is an shortcoming in the frontend

Task rejection as Requestor

I agree that the validation system is important, I will try to include it in the V2 architecture

only the active and the last batch of a campaign can be deleted for
now. else the campaign could end up in a dead lock
we are looking for a way to keep submissions in the same order as
reservations. previously the code falsely assumed that
`available_primary_key` would never re-use keys, but it appears EOS
does (it takes highest_primary_key + 1). this was causing collisions
between submissions IDs
it appears to be an offset-by-1 issue, so this is an attempt to remedy that
jeisses and others added 19 commits April 6, 2024 00:39
there was a change in the output of nodeos RPC calls, where maps
changed from "second" to "value" to refer to their value paramter
these structs where solely needed to generate signature for BSC
addresses, which has now been deprecated
the field can also no longer be optional, as this is not needed with
the new split between reservations and submissions.
To achieve this, we have to keep track of the active batch for each
user inside `acctaskidx`. This means existing `acctaskidx` data will
be corrupted and must be purged.
before we were storing a uint64 (concatenation of campaign_id and
batch_idx). but as campaign_id is already present, we can save space
by just storing batch_idx. it is also more convenient for clients, as
there is no need to disect the batch_id in order to find the batch
index.
fix detecting of the last active cycle, and rely on pre-creating cycle entries
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants