📖
Dev Documents
  • README
  • Basic Concepts
    • TEA Developer Prerequisites
    • The TEA Economic Revolution for Developers
    • The Future of Layer-2s
    • What Makes a Web3 App?
    • Magic of the State Machine
  • Step by Step Tutorial
    • Install Dev Environment
    • Hello World
      • Step 1: Build sample-actor and Run Unit Test
      • Step 2: Start the Local Dev Environment
      • Sample Actor Code Walkthrough
      • Sample Front-end Code Walkthrough
      • 025_understand_request_and_response
    • Deploy Hello World on Testnet
    • Add Login Feature
      • Sample-actor Code Walkthrough - Login Branch
        • tea_sdk_utils
      • Sample Front-end Walkthrough - Login Branch
    • SQL
      • Sample Txn Executor
      • Sample Actor
      • Sample Front-end
    • Reward Fund Transfer
      • Sample Txn Executor
    • Retweet Task
      • Retweet Frontend
      • Retweet Sample Actor
      • Retweet Txn Executor
      • Retweet FAQ
    • Gas Fees
      • Query logs
      • A deep dive into gas measurement and settlement
    • Summary
  • Billing
    • Billing FAQ
    • Gas Fee Billing
    • Gas & Fuse Limits
    • Local Debugging Environment
    • State Maintainer Billing
    • TApp Billing
  • Example TApps
  • Advanced TApps
    • TEA Party TApp Intro
    • TEA Party Code Walkthrough
  • Functions
    • Actors vs Functions
    • Function Calls Between Native & Wasm
    • Native vs Wasm Functions
  • Glossary
    • Actor
    • Adapter
    • App AES Key
    • AuthKey
    • back_end_actor
    • Birth Control
    • Blockchain Listener
    • Capability
    • CML Auctions
    • Commands
    • Consensus
    • Context
    • Conveyor
    • Conveyor: Mutable vs Immutable
    • enclave
    • Followup
    • Front-end
    • GlueSQL
    • GPS
    • Hosting Actor Handlers
    • Hosting CML
    • hosting_profitability
    • Magic of WASM
    • mini-runtime
    • OrbitDb
    • Order of Txns
    • party-actor
    • party-fe
    • Party-state-actor
    • Providers
    • Public Service
    • queries
    • Remote Attestation
    • Staking to Hosting CML
    • Staking to TApp
    • State
    • State Machine
    • State Machine Actor
    • State Machine Replica
    • TEA ID
    • TPM
    • Transactions
    • VMH - Virtual Messaging Hub
    • Where Messages are Stored
Powered by GitBook
On this page
  1. Step by Step Tutorial
  2. Reward Fund Transfer

Sample Txn Executor

In the newly added account.rs file we handle the following txns:

  • deposit_for_task: Pay deposit when taking a task.

  • rollback_deposit: Move the deposit to the owner in case of "reject" operation.

  • reward_owner: The successful worker takes the reward.

All the function code is straightforward and follows the same pattern:

  • Input a mut ctx.

  • Get the amount as well as payer and payee addresses.

  • Call the move or cross_move function to transfer funds.

  • ctx is mutable, so it contains all state changes. The caller will handle the ctx.

Let's move to the caller, the txn.rs. We use the TakeTask as an example, but all others txns follow the same pattern:

Txns::TakeTask {
            subject,
            worker,
            auth_b64,
        } => {
            let task = task_by_subject(subject).await?;
            if task.status != Status::New {
                return Err(TxnErrors::TakeTaskFailed.into());
            }
            if let Some(worker) = task.worker {
                return Err(TxnErrors::TaskInprogress(task.subject, worker).into());
            }
            check_account(auth_b64, *worker).await?;
            let glue_ctx = new_gluedb_context().await?;

            let (tappstore_ctx, ctx) =
                account::deposit_for_task(tsid, base, *worker, task.required_deposit, ctx).await?;
            take_task(tsid, subject, *worker, task.required_deposit).await?;
            CommitContextList {
                ctx_list: vec![
                    CommitContext::new(
                        ctx,
                        glue_ctx,
                        None,
                        None,
                        decode_auth_key(auth_b64)?,
                        txn.to_string(),
                    ),
                    CommitContext::ctx_receipting(tappstore_ctx, txn.to_string()),
                ],
                ..Default::default()
            }
        }

First, get the task. If the task status is not "new" then throw an error because only new tasks can be taken.

If the task has a worker, throw an error because the task has someone working on it.

check_account(auth_b64, *worker).await?; guard the current login user as the input worker parameter.

let glue_ctx = new_gluedb_context().await?; starts an SQL transaction. We should use this preface line everytime we run or process any SQL transactions.

let (tappstore_ctx, ctx) =
                account::deposit_for_task(tsid, base, *worker, task.required_deposit, ctx).await?;

This function deposit_for_task has a ctx input and also outputs tappstore_ctx and ctx. This is very important. Ctx is the object that records all the changes the code logic "should" apply to the state. But until commited, the changes are only stored in ctx without actually modifying the state. If anything is wrong during the ctx execution, the code can simply throw an error and return, and no changes will be applied to the state (i.e. the state remains unchanged).

In order to keep all changes in ctx and commit at once to modify ctx at a later time, ALL functions will have ctx as an input as well as returning ctx to the caller.

In this function, the output has another tappstore_ctx variable rather than the sole input ctx. That's because the tappstore's state will be changed during the execution. We'll need to commit both ctx for this TApp AND the context of TAppStore. Why will we change the state of the TAppStore? It's because the deposit and TEA account balance are stored in the TAppStore state. This is designed for easier TApp development. In most cases, the TApp doesn't need to maintain a TEA token state in their own state. Nor does the user need to topup TEA tokens to all the TApps they're using.

take_task is a function to execute sql scripts. The code is in sql.rs:

pub(crate) async fn take_task(
    tsid: Tsid,
    subject: &str,
    worker: Account,
    required_deposit: Balance,
) -> Result<()> {
    exec_sql(
        tsid,
        format!(
            r#"
            UPDATE Tasks SET 
                status = '{}',worker = '{worker:?}' 
                WHERE subject = '{subject}';
            INSERT INTO TaskExecution VALUES (
                '{subject}', '{worker:?}', '{required_deposit}'
            );
               "#,
            Status::InProgress
        ),
    )
    .await
}

This function is used to change the table to mark that a worker has taken a specific task.

            CommitContextList {
                ctx_list: vec![
                    CommitContext::new(
                        ctx,
                        glue_ctx,
                        None,
                        None,
                        decode_auth_key(auth_b64)?,
                        txn.to_string(),
                    ),
                    CommitContext::ctx_receipting(tappstore_ctx, txn.to_string()),
                ],
                ..Default::default()
            }

All matching branches in the txn executor will need to return such a CommitContextList. This list includes all the changes during the execution. Eventually, these changes will be applied using a Commit function so that the state and SQL database can be permanently changed.

For the details of CommitContextList, please refer to the Developer Documents.

Other minor changes

In error.rs, you can find a few newly added errors that are self-explanatory.

We won't describe table.sql because these are all standard SQL scripts, for indexing, table creating etc. SQL is not in the scope of this tutorial.

PreviousReward Fund TransferNextRetweet Task

Last updated 2 years ago

Lastly, at the end of this , return the CommitContextList:

txn