📖
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
  • Party-fe
  • bbs.vue common requests functions
  • txn_request
  • uuid
  • txn_hash
  • Workflow
  1. Glossary

party-fe

Party-fe

The code is located at https://github.com/tearust/tapp-sample-teaparty/tree/demo-code/party-fe. You can clone the code to a local repo to make it easier to go through.

This is a standard Vue application. We assume that readers are familar with VUE and front-end web technologies. Below we'll only focus on the TEA related parts.

bbs.vue common requests functions

This file https://github.com/tearust/tapp-sample-teaparty/blob/demo-code/party-fe/src/views/bbs.js handles most message-related user interactions.

For example, the code snippets below are handling a load message and send message request:

async loadMessageList(address, channel=default_channel){
    // F.top_log("Query message list...");
    const rs = await _axios.post('/tapp/loadMessageList', {
      tappId: F.getTappId(),
      channel: F.getChannel(channel),
      address: '',
    });

    // F.top_log(null);

    if(!rs) return [];

    return F.formatMessageList(JSON.parse(rs));

  },
  async updateTappProfile(address){
    const user = F.getUser(address);
    if(!user || !user.isLogin){
      throw 'not_login';
    }
    // TODO if user is not owner, return;

    const opts = {
      tappId: F.getTappId(),
      address,
      authB64: user.session_key,
      postMessageFee: 100,
    };
    const rs = await sync_request('updateTappProfile', opts);
    console.log('updateTappProfile => ', rs);
    return rs;
  },
  async sendMessage(address, msg, channel=default_channel, ttl=null){
    const user = F.getUser(address);
    if(!user || !user.isLogin){
      throw 'Not login';
    }
    
    msg = utils.forge.util.encodeUtf8(msg);
    const encrypted_message = utils.forge.util.encode64(msg);
    // console.log(121, utils.crypto.encode(address, msg));
    
    // const decode_msg = utils.crypto.decode(address, utils.forge.util.decode64(encrypted_message));
    // console.log('decode_msg => '+decode_msg);

    const opts = {
      tappId: F.getTappId(),
      address,
      channel: F.getChannel(channel),
      // message: msg
      encryptedMessage: encrypted_message,
      authB64: user.session_key,
      ttl,
    };
console.log('message => ', opts)
    let rs = null;
    if(opts.channel === 'test'){
      // free msg
      rs = await _axios.post('/tapp/postFreeMessage', {
        ...opts,
        uuid: uuid(),
      });
    }
    else{
      const txn = require('./txn').default;
      rs = await txn.txn_request('postMessage', opts);
    }
    
    return rs;
  },

You probably have noiticed that the most improtant line related to TEA is the line await _axios.post('/tapp/loadMessageList' for querying mesages, and this line `await txn.txn_request('postMessage', opts); for posting message.

txn_request

the _axios.post is a standard http call which doesn't need to be explained. We can focus on the txn.txn_request utility function.

The code is under views/txn.js. Almost the entire file is comprised of this function.

import {_, axios, moment, uuid} from 'tearust_utils';
import utils from '../tea/utils';
import bbs from './bbs';

const F = {

  async txn_request(method, param){
    const _uuid = uuid();
    console.log("prepare for txn: ", method, _uuid);
    
    const _axios = bbs.getAxios();

    const txn_uuid = 'txn_'+_uuid;
    try{
      bbs.log("Send txn request...");
      console.log("Send txn request...");
      const step1_rs = await _axios.post('/tapp/'+method, {
        ...param,
        uuid: txn_uuid,
      });
      console.log("step_1 result: ", step1_rs);
    }catch(e){
      console.error("step_1 error: ", e);

      throw e;
    }

    bbs.log('Wait for query txn hash...');
    console.log('Wait for query txn hash...');
    await utils.sleep(5000);

    let step_2_rs = null;
    const step_2_loop = async ()=>{
      try{
        console.log('query result for '+txn_uuid+'...');
        step_2_rs = await _axios.post('/tapp/query_result', {
          uuid: txn_uuid,
        });

        step_2_rs = utils.parseJSON(step_2_rs);
      }catch(e){
        console.log("step2 error: ", e);
        // rs = e.message;
        step_2_rs = null;
        await utils.sleep(3000);
        await step_2_loop();
      }
  
    };
  
    bbs.log("Start to query txn result...");
    console.log("Start to query txn result...");
    await step_2_loop();

    console.log("step2 result: ", step_2_rs);

    bbs.log('Wait for next step...');
    console.log('Wait for next step...');
    utils.sleep(5000);

    const step_3_hash = step_2_rs.hash;
    const hash_uuid = "hash_"+_uuid;
    let step_3_rs = null;
    let step_4_rs = null;
    let sn = 0;
    const step_4_loop = async ()=>{
      if(sn > 10) {
        step_4_rs = {
          'status': false,
          'error': 'request timeout',
        };
        return;
      }
      try{
        bbs.log("Send query txn hash request...");
        console.log('Send query txn hash request...');
        step_3_rs = await _axios.post('/tapp/queryHashResult', {
          hash: step_3_hash,
          uuid: hash_uuid,
        });
    
        bbs.log('Wait for query txn hash result...');
        console.log('Wait for query txn hash result...');
        await utils.sleep(5000);

        console.log('query hash result for '+hash_uuid+'...');
        step_4_rs = await _axios.post('/tapp/query_result', {
          uuid: hash_uuid,
        });

        step_4_rs = utils.parseJSON(step_4_rs);
        if(!step_4_rs.status) throw step_4_rs.error;
      }catch(e){
        console.log("step4 error: ", e);

        if(e !== 'wait'){
          throw e;
        }
        
        // rs = e.message;
        step_4_rs = null;
        sn++;
        await utils.sleep(5000);
        await step_4_loop();
      }
  
    };
  
    bbs.log("Start to query hash result...");
    console.log("Start to query hash result...");
    await step_4_loop();

    console.log("step4 result: ", step_4_rs);
    if(step_4_rs.error){
      throw step_4_rs.error;
    }

    if(!step_4_rs.need_query){
      return step_4_rs;
    }

    // continue query

    let step_5_rs = null;
    let step_5_uuid = step_4_rs.query_uuid || _uuid;
    let step_5_n = 0;
    const step_5_loop = async ()=>{
      if(step_5_n > 3){
        throw 'query timeout...';
      }
      try{
        console.log('continue query for '+step_5_uuid+'...');
        step_5_rs = await _axios.post('/tapp/query_result', {
          uuid: step_5_uuid,
        });

        step_5_rs = utils.parseJSON(step_5_rs);
      }catch(e){
        console.log("step5 error: ", e);
        step_5_n ++;
        step_5_rs = null;
        await utils.sleep(5000);
        await step_5_loop();
      }
    };

    bbs.log("Start to query action result...");
    console.log("Start to query action result...");
    await step_5_loop();
    console.log("step5 result: ", step_5_rs);

    const rs = step_5_rs;

    return rs;
  }

};


export default F;

This is a big function, so let's dig into it step by step.

uuid

Before the first step, we generate a UUID. This is used for a future results query (ecause all txns are async calls). You're not supposed to get a response immediately. You have to query after a period of time, and then from time to time until you get the result: either success or fail. UUID is the handle for such queries.

const step1_rs = await _axios.post('/tapp/'+method, {
        ...param,
        uuid: txn_uuid,
      });

txn_hash

The result from step1 doesn't mean anything. It just says "hey I accepted your txn request". In order to query the result of such a txn, we need to have the hash of that txn. We don't know it at this moment. The only thing we know is the UUID. So the step2 should query the txn_hash using the UUID. You can see the code below:

let step_2_rs = null;
    const step_2_loop = async ()=>{
      try{
        console.log('query result for '+txn_uuid+'...');
        step_2_rs = await _axios.post('/tapp/query_result', {
          uuid: txn_uuid,
        });

        step_2_rs = utils.parseJSON(step_2_rs);
      }catch(e){
        console.log("step2 error: ", e);
        // rs = e.message;
        step_2_rs = null;
        await utils.sleep(3000);
        await step_2_loop();
      }
  
    };
step_3_rs = await _axios.post('/tapp/queryHashResult', {
          hash: step_3_hash,
          uuid: hash_uuid,
        });
    
step_4_rs = await _axios.post('/tapp/query_result', {
          uuid: hash_uuid,
        });

        step_4_rs = utils.parseJSON(step_4_rs);
        if(!step_4_rs.status) throw step_4_rs.error;
      }catch(e){
        console.log("step4 error: ", e);

        if(e !== 'wait'){
          throw e;
        }
        

For most txns, as long as step4 has received the answer, the whole process is done. But for some txns, there are some follow-up tasks after the result. This is what step5 is supposed to do.

let step_5_rs = null;
    let step_5_uuid = step_4_rs.query_uuid || _uuid;
    let step_5_n = 0;
    const step_5_loop = async ()=>{
      if(step_5_n > 3){
        throw 'query timeout...';
      }
      try{
        console.log('continue query for '+step_5_uuid+'...');
        step_5_rs = await _axios.post('/tapp/query_result', {
          uuid: step_5_uuid,
        });

        step_5_rs = utils.parseJSON(step_5_rs);
      }catch(e){
        console.log("step5 error: ", e);
        step_5_n ++;
        step_5_rs = null;
        await utils.sleep(5000);
        await step_5_loop();
      }

Now the whole txn workflow is completed.

Workflow

To make the workflow clear and visual, let's draw a sequence diagram.

Previousparty-actorNextParty-state-actor

Last updated 2 years ago

You might have noticed that this line await _axios.post('/tapp/postFreeMessage', also looks like it's sending a command, but why is it not using txn.txn_request()? Well, posting a message does look like a command, but the free message doesn't cost anything. Therefore there is no state change (no money transfer). It can be comfortably handled by the alone without notifying the . No matter if it's or , they are concepts related to the state machine and not your application.

Once the UUID is confirmed, it uses an http call to the like this:

The will handle this txn and run the back-end logic accordingly. If you are interested in that part, go to .

The result of step2 is the txn_hash. Now the front-end has the txn hash and the back-end has sent the txn to the . But we haven't got the result yet. In order to get the result, the front-end needs to ask the to initialize a series of to the to get the result of the transaction. Step3 performs this "initialization" request.

Now, the receives the request and starts querying the for the result. Because this is an async call, the back-end cannot get the result immediately. It will keep polling several times to get the result. When the back-end receives the result, it will cache it in memory for a short period of time, waiting for the to fetch it. Step4 actually did the "fetching" job.

There are a few meaningful parameters in the step_ r_rs result. For example, do I need to wait and query again? This happens if the has not received the result from the yet.

hosting_cml
state machine
queries
commands
hosting_cml
hosting_cml
back_end_actor
State_Machine
back_end_actor
queries
State_Machine
back_end_actor
State_Machine
front_end
back_end_actor
State_Machine