Save
Saving
  • R
    raymond

    Step2:Write DApp

    There are two Dapps to be written, one is MiningDapp, the other is UDapp. We use the Javascript SDK U3.js to write the DApps. To be simple, we create the two DApps in one project.

    1. Written by DApp

    In WebStorm, create a new project -> Node.js Express App-> Template select Pug (Jade); modify the project name to CarbonDApp;
    0_1543574883406_4a8bb295-4898-4c8a-a81f-f71152a03bbf-image.png

    Click ‘Create’ to create a project

    1. Import project dependent class library

    Copy all the directories in node_modules in CarbonProject to the node_modules directory of CarbonDApp, and repeat the directory skipping without copying;
    Copy the CarbonDApp/node_modules/u3.js/dist/u3.js file to the CarbonDApp/public/javascripts directory.

    1. Modify the DApp server port

    Since the default 3000 port is already occupied, you need to modify the server port of the DApp.

    Open the bin/www file and find var port = normalizePort(process.env.PORT || '3000'); change 3000 to 3001

    1. Create an account information browsing page

    Create an index.html page in the public directory and add the following code to the page:

    <html>
    
    <head>
      <meta charset="UTF-8">
      <title>test</title>
        <link rel="stylesheet" href="/stylesheets/style.css">
      <script src="./javascripts/u3.js"></script>
      <script>
    
          let u3 = U3.createU3({
              httpEndpoint: 'http://127.0.0.1:8888',
              httpEndpoint_history: 'http://127.0.0.1:3000',
              broadcast: true,
              debug: false,
              sign: true,
              logger: {
                  log: console.log,
                  error: console.error,
                  debug: console.log
              },
              chainId:'2616bfbc21e11d60d10cb798f00893c2befba10e2338b7277bb3865d2e658f58',
              symbol: 'UGAS'
          });
    
    
          async function getBalanceInfo() {
              let SYMBOL = 'CARB';
              let account = 'ben';
    
              const ben_balance = await u3.getCurrencyBalance({
                  code:'ben',
                  symbol: SYMBOL,
                  account: account
              });
    
              document.getElementById("carbon_balance").innerHTML = '账户余额:'+ben_balance;
    
              const bob_balance = await u3.getCurrencyBalance({
                  code:'ben',
                  symbol: SYMBOL,
                  account: 'bob'
              });
    
              document.getElementById("bob_balance").innerHTML = '账户余额:'+ bob_balance;
    
              const tom_balance = await u3.getCurrencyBalance({
                  code:'ben',
                  symbol: SYMBOL,
                  account: 'tom'
              });
    
              document.getElementById("tom_balance").innerHTML = '账户余额:'+tom_balance;
    
              const tony_balance = await u3.getCurrencyBalance({
                  code:'ben',
                  symbol: SYMBOL,
                  account: 'tony'
              });
    
              document.getElementById("tony_balance").innerHTML = '账户余额:'+tom_balance;
    
              const jerry_balance = await u3.getCurrencyBalance({
                  code:'ben',
                  symbol: SYMBOL,
                  account: 'jerry'
              });
    
              document.getElementById("jerry_balance").innerHTML = '账户余额:'+tom_balance;
    
              const jack_balance = await u3.getCurrencyBalance({
                  code:'ben',
                  symbol: SYMBOL,
                  account: 'jack'
              });
    
              document.getElementById("jack_balance").innerHTML = '账户余额:'+tom_balance;
          }
    
          getBalanceInfo();
          //transfer();
      </script>
    </head>
    
    <body>
    
        <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
            <h1>CarbonCoin发行方</h1>
            <p>账户:ben</p>
            <p>描述:进行CarbonCoin发行的发行方,持有所有未分配的CarbonCoin</p>
            <p id="carbon_balance"></p>
    
        </div>
    
        <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
            <h1>节能设备1</h1>
            <p>账户:bob</p>
            <p>描述:通过节能产生CarbonCoin的节能设备1</p>
            <p id="bob_balance"></p>
        </div>
    
        <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
            <h1>节能设备2</h1>
            <p>账户:tom</p>
            <p>描述:通过节能产生CarbonCoin的节能设备2</p>
            <p id="tom_balance"></p>
        </div>
    
        <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
            <h1>航空公司C</h1>
            <p>账户:tony</p>
            <p>描述:大型航空公司,化石能源使用大户</p>
            <p id="tony_balance"></p>
    
        </div>
    
        <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
            <h1>汽车制造商D</h1>
            <p>账户:jerry</p>
            <p>描述:大型汽车制造商,化石能源使用大户</p>
            <p id="jerry_balance"></p>
    
        </div>
    
        <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
            <h1>CarbonCoin销毁账户</h1>
            <p>账户:jack</p>
            <p>描述:用于CarbonCoin销毁的账户,持有所有销毁的CarbonCoin</p>
            <p id="jack_balance"></p>
    
        </div>
    
    </body>
    
    </html>
    
    1. Start the DApp service, test the page
      In the npm interface, double click the start button
      0_1543575306907_fffec1d3-dc9d-43b9-b9e2-ec9062a5930e-image.png

    After the server is successfully started, visit http://127.0.0.1:3001/index.html and the following page appears:
    0_1543575357418_cd5e602c-f181-41e9-8a8e-08c9afb8b70a-image.png

    It can be seen that the issuer ben account holds 10 million of all CARB Tokens issued; other accounts have not yet held CARB Token;

    1. Create a mining.html page

    Mining.html page, energy-saving device 1 and energy-saving device 2 send the calorie value generated in the first 30 seconds to the energy smart contract every 30 seconds, and redeem CarbonCoin;

    Create a mining.html page in the public directory, enter the following code:

    <html>
    
    <head>
        <meta charset="UTF-8">
        <title>test</title>
        <link rel="stylesheet" href="/stylesheets/style.css">
        <script src="./javascripts/u3.js"></script>
        <script>
    
            let u3_bob = U3.createU3({
                httpEndpoint: 'http://127.0.0.1:8888',
                httpEndpoint_history: 'http://127.0.0.1:3000',
                broadcast: true,
                debug: false,
                sign: true,
                logger: {
                    log: console.log,
                    error: console.error,
                    debug: console.log
                },
                chainId:'2616bfbc21e11d60d10cb798f00893c2befba10e2338b7277bb3865d2e658f58',
                keyProvider: '5JoQtsKQuH8hC9MyvfJAqo6qmKLm8ePYNucs7tPu2YxG12trzBt',//bob's private_key
                symbol: 'CARB'
            });
    
            let u3_tom = U3.createU3({
                httpEndpoint: 'http://127.0.0.1:8888',
                httpEndpoint_history: 'http://127.0.0.1:3000',
                broadcast: true,
                debug: false,
                sign: true,
                logger: {
                    log: console.log,
                    error: console.error,
                    debug: console.log
                },
                chainId:'2616bfbc21e11d60d10cb798f00893c2befba10e2338b7277bb3865d2e658f58',
                keyProvider: '5KXYYEWSFRWfNVrMPaVcxiRTjD9PzHjBSzxhA9MeQKHPMxWP8kU',//tom's private_key
                symbol: 'CARB'
            });
    
            function wait(ms = 0) {
                return new Promise(res => setTimeout(res, ms));
            }
    
            function randomFrom(lowerValue,upperValue)
            {
                return Math.floor(Math.random() * (upperValue - lowerValue + 1) + lowerValue);
            }
    
            async function mining() {
    
                let owner_account = 'ben';
    
                const tr_bob = await u3_bob.contract(owner_account);
                const tr_tom = await u3_tom.contract(owner_account);
    
                // let amount = U3.format.UDecimalPad(2000,4);
                let heat_bob = randomFrom(15000,20000);
                let heat_tom = randomFrom(15000,20000);
                await tr_bob.recordHeat(heat_bob,{ authorization: [`[email protected]`] });
                await tr_tom.recordHeat(heat_tom,{ authorization: [`[email protected]`] });
    
                var d=new Date();
                var t=d.toLocaleTimeString();
    
                document.getElementById("bob_log").innerHTML = 'time :'+t+', heat value :'+ heat_bob;
    
                document.getElementById("tom_log").innerHTML = 'time :'+t+', heat value :'+ heat_tom;
            }
    
            mining();
            var int=self.setInterval("mining()",30*1000);
        </script>
    </head>
    
    <body>
    
    <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
        <h1>节能设备1</h1>
        <p>账户:bob</p>
        <p>描述:通过节能产生CarbonCoin的节能设备1</p>
        <p id="bob_log"></p>
    </div>
    
    <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
        <h1>节能设备2</h1>
        <p>账户:tom</p>
        <p>描述:通过节能产生CarbonCoin的节能设备2</p>
        <p id="tom_log"></p>
    </div>
    
    <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
        <button onclick="int=window.clearInterval(int)">stop</button>
    </div>
    
    </body>
    
    </html>
    

    Go to http://127.0.0.1:3001/mining.html and the following page appears:

    0_1543575538498_48829722-8dbc-4c58-bb35-276564f71ece-image.png

    There are two things to note here.

    • When sending data to the energy smart contract, you need to hold the private key of the account before you can sign and send it. In order to simplify the programming, the private key is directly written to the webpage. In the actual program, the private key needs to be protected and hidden.

    • To simplify the writing, the Bob and tom DApp programs are written directly to a page. Actually, they should be two DApp program instances, which are running on two devices respectively.

    Now our energy-saving equipment can already send data to the smart contract, and after the energy smart contract receives the data, it will pass the data to the operator server, and the operator server will call the energy smart contract again to exchange carboncoin. So we need to establish EnergyServer;

    Step3: EnergyServer

    1. Establish event monitor and handle events

    In this section, we will deal with the above calorific value data upload event.

    1.1 Establish listener event server code

    In the bin directory of the CarbonDApp project, create a new file msgBroker.js and enter the following code.

    const { createU3 , format} = require('u3.js/src');
    
    const http = require('http');
    const port = 3002;
    
    let u3 = createU3({
        httpEndpoint: 'http://127.0.0.1:8888',
        httpEndpoint_history: 'http://127.0.0.1:3000',
        broadcast: true,
        debug: false,
        sign: true,
        logger: {
            log: console.log,
            error: console.error,
            debug: console.log
        },
        chainId:'2616bfbc21e11d60d10cb798f00893c2befba10e2338b7277bb3865d2e658f58',
        keyProvider:'5JbedY3jGfNK7HcLXcqGqSYrmX2n8wQWqZAuq6K7Gcf4Dj62UfL',//ben's private key
        symbol: 'UGAS'
    });
    
    
    let server = http.createServer((request, response) => {
    
        const { headers, method, url } = request;
        console.log(method);
        console.log(url);
        let body = [];
        request.on('error', (err) => {
            console.error(err);
        }).on('data', (chunk) => {
            body.push(chunk);
        }).on('end', () => {
            body = Buffer.concat(body).toString();
    
            console.log("received msg:", body);
    
            if (body != null && body.trim()!="") {
    
                var obj = JSON.parse(body);
    
                let heatValue = JSON.parse(obj[1]).heat.split(",")[0];
                let account = JSON.parse(obj[1]).heat.split(",")[1];
    
                exchangeCarbonCoin(account,heatValue);
            }
    
    
            response.on('error', (err) => {
                console.error(err);
            });
    
            response.statusCode = 200;
            response.setHeader('Content-Type', 'application/json');
            response.write("ok");
            response.end();
    
        });
    
    });
    
    server.keepAliveTimeout = 0;
    server.timeout = 0;
    
    server.listen(port, function () {
        console.log((new Date()) + " Server is listening on port " + port);
    });
    
    function wait(ms = 0) {
        return new Promise(res => setTimeout(res, ms));
    }
    
    async function exchangeCarbonCoin(account,heat) {
        let SYMBOL = 'CARB';
        let code = 'ben';
    
        const tr = await u3.contract(code);
    
        let heatValue = format.DecimalPad(heat,4);
    
        const result = await tr.exchangeCarbonCoin('ben', account, heatValue+' ' + SYMBOL, 'test', { authorization: [`[email protected]`] });
    
        let tx = await u3.getTxByTxId(result.transaction_id);
        while (!tx.irreversible) {
            await wait(1000);
            tx = await u3.getTxByTxId(result.transaction_id);
            if (tx.irreversible) {
                console.log(tx);
                break;
            }
        }
    }
    

    The code establishes a service on port 3002. After the energy-saving device uploads its calorific value, the smart contract will send the energy-saving device to the service to upload the calorific value data. The service calls the exchangeCarbonCoin method of the energy smart contract to exchange the CarbonCoin.

    1.2 Establish a listener event server startup script

    Open the CarbonDApp/package.json file and add the following code to the scripts property: "event": "node ./bin/msgBroker"

    1.3 Start the listening server

    Refresh the npm window with the ‘event’ option

    0_1543575702002_4033a480-44d2-4c54-b51e-cf89ac6728bd-image.png

    Double-click event to complete server startup

    0_1543575729850_8f2d31ee-8ac0-4907-93cb-a5a07f8e206b-image.png

    1.4 Register event monitoring mechanism on the chain

    Query the internal network address of the machine through the ifconfig command, for example: 10.20.8.32

    In the CarbonProject/test directory, create the registerEvent.js file; enter the following code in the file

    const U3Utils = require('u3-utils/dist/es5');
    const { createU3, format, listener } = require('u3.js/src');
    const config = require('../config');
    
    const chai = require('chai');
    require('chai')
        .use(require('chai-as-promised'))
        .should();
    
    const should = chai.should();
    const expect = chai.expect;
    const assert = chai.assert;
    
    describe('Test cases', function() {
    
        it('event register', async () => {
    
            let account = 'ben';
            const u3 = createU3(config);
            await u3.registerEvent(account, 'http:// 10.20.8.32:3002');
    
            U3Utils.wait(1000);
    
        });
    
    
        it('event unregister', async () => {
    
            let account = 'ben';
            const u3 = createU3(config);
            await u3.unregisterEvent(account, ' http:// 10.20.8.32:3002');
    
            U3Utils.wait(1000);
        });
    });
    

    Note that the registered address is modified to be the internal network address of the machine, and 127.0.0.1 cannot be used.
    Run the event register test case to complete the event registration;

    0_1543575827595_65d03539-1e3a-4694-a2f0-1a4fa509efc4-image.png

    1.5 Test calorie value for CarbonCoin function

    Visit http://127.0.0.1:3001/mining.html page, every 30 seconds, both bob and tom will randomly send a random value between 15000 and 20000 as the calorific value to convert the smart contract to CarbonCoin;
    Visit and refresh the http://127.0.0.1:3001/index.html page, you can find that the CarbonCoin account of bob and tom is increasing every 30 seconds;

    Step4:Improve DApp functionality

    1. Add transfer function

    After the energy-saving equipment acquires CarbonCoin, it can sell CarbonCoin to the enterprise, such as airlines, etc. The simple embodiment is that the equipment account can be transferred to the corporate company account;

    Open the /CarbonDApp/index.html page and add the code

    1.1 U3 object used when establishing the transfer

    let u3_bob = U3.createU3({
        keyProvider:'5JoQtsKQuH8hC9MyvfJAqo6qmKLm8ePYNucs7tPu2YxG12trzBt',//bob's private key
    })
    
    let u3_tom = U3.createU3({
        keyProvider:'5KXYYEWSFRWfNVrMPaVcxiRTjD9PzHjBSzxhA9MeQKHPMxWP8kU',//tom's private key
    })
    

    1.2 Add the js code of the transfer to call the corresponding smart contract method

    async function sendCoin(from,to){
    
        let SYMBOL = 'CARB';
        let code = 'ben';
    
        let coins = U3.format.DecimalPad(this.randomFrom(100,500),4);
    
        if (from == 'bob'){
            const tr = await u3_bob.contract(code);
            const result = await tr.transfer(from, to, coins + ' ' + SYMBOL, 'sendCoin', { authorization: [`[email protected]`] });
    
            let tx = await u3_bob.getTxByTxId(result.transaction_id);
            while (!tx.irreversible) {
                await wait(1000);
                tx = await u3_bob.getTxByTxId(result.transaction_id);
                if (tx.irreversible) {
                    alert("bob send coin success:"+ coins);
                    break;
                }
            }
        } else if (from == 'tom'){
            const tr = await u3_tom.contract(code);
            const result = await tr.transfer(from, to, coins + ' ' + SYMBOL, 'sendCoin', { authorization: [`[email protected]`] });
    
            let tx = await u3_tom.getTxByTxId(result.transaction_id);
            while (!tx.irreversible) {
                await wait(1000);
                tx = await u3_tom.getTxByTxId(result.transaction_id);
                if (tx.irreversible) {
                    alert("tom send coin success:"+ coins);
                    break;
                }
            }
        }
    }
    

    1.3 Add a call to the sendcoin() method on the page, update the following code

    <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
        <h1>节能设备1</h1>
        <p>账户:bob</p>
        <p>描述:通过节能产生CarbonCoin的节能设备1</p>
        <p id="bob_balance"></p>
        <p><a href="#" OnClick="sendCoin('bob','tony')">给航空公司C转账</a></p>
    </div>
    
    <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
        <h1>节能设备2</h1>
        <p>账户:tom</p>
        <p>描述:通过节能产生CarbonCoin的节能设备2</p>
        <p id="tom_balance"></p>
        <p><a href="#" OnClick="sendCoin('tom','jerry')">给汽车制造商D转账</a></p>
    </div>
    

    1.4 Access the page and test the function

    Visit: http://127.0.0.1:3001/index.html, click the "Transfer to the airline C" button, wait for a period of time (up to about 10 seconds, confirm the time for the Ultrain transaction), the following prompt appears, indicating the transfer success.

    0_1543576100158_41126a00-6111-4842-bdd5-269a5f993c7d-image.png

    1. Add a company to convert CarbonCoin to a charity point function

    By converting CarbonCoin into charitable points, the company makes its own contribution to reducing carbon emissions and enhances its reputation:

    Open the /CarbonDApp/index.html page and add the code

    2.1 U3 object used when establishing point redemption

    let u3_tony = U3.createU3({
        keyProvider:'5KbHvFfDXovPvo2ACNd23yAE9kJF7Mxaws7srp6VapjMr7TrHZB',//tony's private key
    })
    
    let u3_jerry = U3.createU3({
        keyProvider:'5JFz7EbcsCNHrDLuf9VpHtnLdepL4CcAEXu7AtSUYfcoiszursr',//jerry's private key
    })
    

    2.2 Add the redeemed js code to call the corresponding smart contract method

    async function exchangeScore(account){
        let SYMBOL = 'CARB';
        let code = 'ben';
        let to = 'jack';
    
        let coins = U3.format.DecimalPad(this.randomFrom(10,50),4);
    
        if (account == 'tony'){
            const tr = await u3_tony.contract(code);
            const result = await tr.exchangeScore(account, to, coins + ' ' + SYMBOL, 'sendCoin', { authorization: [`[email protected]`] });
    
            let tx = await u3_tony.getTxByTxId(result.transaction_id);
            while (!tx.irreversible) {
                await wait(1000);
                tx = await u3_tony.getTxByTxId(result.transaction_id);
                if (tx.irreversible) {
                    alert("tony buy score success:"+ coins);
                    break;
                }
            }
        } else if (account == 'jerry'){
            const tr = await u3_jerry.contract(code);
            const result = await tr.exchangeScore(account, to, coins + ' ' + SYMBOL, 'sendCoin', { authorization: [`[email protected]`] });
    
            let tx = await u3_jerry.getTxByTxId(result.transaction_id);
            while (!tx.irreversible) {
                await wait(1000);
                tx = await u3_jerry.getTxByTxId(result.transaction_id);
                if (tx.irreversible) {
                    alert("jerry buy score success:"+ coins);
                    break;
                }
            }
        }
    }
    

    2.3 Add a call to the exchangeScore() method on the page, update the following code

    <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
        <h1>航空公司C</h1>
        <p>账户:tony</p>
        <p>描述:大型航空公司,化石能源使用大户</p>
        <p id="tony_balance"></p>
        <p><a href="#" OnClick="exchangeScore('tony');return false;">购买公益积分</a></p>
    
    </div>
    
    <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
        <h1>汽车制造商D</h1>
        <p>账户:jerry</p>
        <p>描述:大型汽车制造商,化石能源使用大户</p>
        <p id="jerry_balance"></p>
        <p><a href="#" OnClick="exchangeScore('jerry');return false;">购买公益积分</a></p>
    
    </div>
    

    2.4 Access the page and test the function

    Visit: http://127.0.0.1:3001/index.html, click the "Buy Charity Points" button, wait for a period of time (up to about 10 seconds, confirm the time for the Ultrain transaction), the following prompt appears, indicating that the points are successfully redeemed. At the same time, you can see the number of CarbonCoins destroyed in the CarbonCoin destruction account at the bottom of the page.

    0_1543576135492_bde5ab3a-fd56-4fee-ae8c-f435849f747c-image.png

    1. Query the public welfare points exchange history

    By enquiring the redemption history of charity points, we can establish a ranking of corporate charity points.

    3.1 Creating a page

    In the CarbonDApp/public/ directory, create a new leaderboard.html and enter the following code:

    <html>
    
    <head>
        <meta charset="UTF-8">
        <title>leaderboard</title>
        <link rel="stylesheet" href="/stylesheets/style.css">
        <script src="./javascripts/u3.js"></script>
        <script>
    
            let u3 = U3.createU3({
                httpEndpoint: 'http://127.0.0.1:8888',
                httpEndpoint_history: 'http://127.0.0.1:3000',
                broadcast: true,
                debug: false,
                sign: true,
                logger: {
                    log: console.log,
                    error: console.error,
                    debug: console.log
                },
                chainId:'2616bfbc21e11d60d10cb798f00893c2befba10e2338b7277bb3865d2e658f58',
                symbol: 'UGAS'
            });
    
            function wait(ms = 0) {
                return new Promise(res => setTimeout(res, ms));
            }
    
            async function getLeaderboard() {
    
                let owner_account = 'ben';
    
                const tr = await u3.contract(owner_account);
    
                const result = await u3.getAllTxs(1,10000000,{"actions.name":"exchangeScore"},{_id:-1});
                //console.log(result);
                let content = '';
                for ( let i = 0 ;i< result.results.length;i++){
                    content = content + '<tr><td>'+ result.results[i].actions[0].data.from+":"+result.results[i].actions[0].data.quantity
                        +'</td></tr>';
                }
    
                document.getElementById("leaderboard").innerHTML = content;
            }
    
            getLeaderboard();
        </script>
    </head>
    
    <body>
    
    <div style="padding:8px;border:1px solid #96c2f1;background:#eff7ff">
        <h1>leaderboard</h1>
    
        <table id="leaderboard">
        </table>
    </div>
    </body>
    </html>
    

    We use the u3.getAllTxs(1,10000000,{"actions.name":"exchangeScore"},{_id:-1}); method to query all redemption transactions and print their specific data to the page.

    3.2 Run page

    Visit http://127.0.0.1:3001/leaderboard.html and you can see the following page:

    0_1543576164049_3ec44bde-b9bd-4065-84df-f65764600362-image.png

    Here is a history of all points redemption.

    Summary

    This tutorial system describes how to write an end-to-end web version of the DApp application. Although it has been simplified in the process of writing, it generally describes the overall picture of DApp application development, developers can do the basis of this framework. Enrich and improve your DApp application.

    About developing DApp on iOS and Android, you can integrate u3.js into the corresponding hybrid Native development framework, and the principle is the same as the web version DApp.

    posted in Development read more
  • R
    raymond

    This tutorial will guide developers to create DApp based on a real business scenario using Ultrain's Robin Framework. In this process, you can get a deep understanding of Ultrain's technical features in DApp development, and quickly learn how to write DApp. You will find it is very easy to write DApp based on the Robin Framework.

    The source code can be found at https://github.com/ultrain-os/UltrainDappDemo

    This tutorial is an intermediate tutorial. Through this tutorial, you can learn the basic concepts of DApp development, the usage of Ultrain Database and triggers;

    Precondition:

    You have already learned the "Quickly writing XXXXX" tutorial, and through the guidelines of this tutorial, you have installed the Ultrain Robin development environment, the local test environment LongCrow;

    System Requirements

    1. Mac operating system (Ultrain now only supports in Mac environment development, based on Linux and Window integrated development environment developing)
    2. IDE:WebStorm
    3. Docker

    Business scenario introduction

    NewEnergy company’s new energy equipment can significantly reduce carbon emissions through the use of new clean energy sources. NewEnergy wants to convert its reduced carbon emissions into carbon coins accordingly. On the one side, the companies using NewEnergy’s equipment can obtain additional income through the carbon coin; on the other hand, companies with more carbon emissions can purchase carbon coins, which can provide financial support for emission reduction, and enhance the public image of the company in terms of carbon emissions; therefore, the scenario has the following Character:

    NewEnergy: the operator of the carbon coin, creating and issuing carbon coins, the Ultrain’s account used by NewEnergy is ben;

    Company A: Using new energy equipment A produced by NewEnergy, in the process of using the equipment, the calorie value consumed will be recorded on the blockchain every 30 seconds, and the value will be converted into carbon coins and distributed to Company A's Ultrain’s account, bob;

    Company B: Using new energy equipment B produced by NewEnergy, in the process of using the equipment, the calorie value consumed will be recorded on the blockchain every 30 seconds, and the value will be converted into carbon coins to be distributed to Company B's Ultrain’s account, tom;

    Company C: Airline, a major consumer of fossil energy, which can enhance the public image by purchasing and consuming carbon coins; the Ultrain account used by Company C is tony

    Company D: Automobile manufacturing company, a large-scale fossil energy consumer, which can enhance the corporate public image by purchasing and consuming carbon coins; the Ultrain account used by Company D is jerry

    In the Ultrain account jack, when exchanging the charity points, CarbonCoin burns the entered address, and the CarbonCoin which enters the address will no longer be used;

    0_1543571484931_fd7d46b3-9172-48fe-9597-5cb81a8b8b2b-image.png
    As shown in the pictures above, the system consists of four programs:

    • Energy Smart Contract: A smart contract is developed based on TypeScript, running on the Ultrain chain;

    • MiningDApp: The terminal DApp is developed based on the U3 framework collecting saving energy data,;

    • UDApp: Exchanging Charity Points DApp is developed based on U3 framework

    • EnergyServer: The server program which monitors and executes the redemption is developed based on the NodeJS framework;

    Step1: Energy Smart Contract Development

    1. Create a project directory and initialize the project environment
    mkdir CarbonProject
    cd CarbonProject
    robin init
    
    1. Install the dependent needed for Ultrain development via npm, and fix project errors via the robin command
    npm install
    robin lint
    

    0_1543571650872_9d47d91e-bd34-4cbb-a6bb-54eb54745e33-image.png

    1. Develop IDE WebStorm using Javascript, and open the project CarbonProject

    Briefly introduce the functions of each directory in the project

    contract: The project's smart contract catalog, where the project's main smart contract is placed, and only one smart contract in each project is compiled, which is the smart contract under the catalog; at the same time, Ultrain now supports only one smart contract per account.

    migrations: Configuration file for smart contract deployment parameters

    node_modules: Project dependent JS class library

    template: Smart contract code for the example

    test: Test code directory, test code is javascript code

    config.js: The default parameters that need to be configured when the U3 framework accesses the Ultrain chain; these parameters can also be dynamically assigned when the U3 framework is created;

    1. Writing Energy Smart Contracts

    a. Delete the MyContract.ts file in the contract directory, copy the template/token/token.ts file to the contract directory, and modify the file name to energy.ts; modify the class name to energy;

    b. Write code that calls energy.ts and releases CarbonCoin

    In the test directory, create a new javascript file named issueCarbonCoin.js

    build code framework

    const U3Utils = require('u3-utils/dist/es5');
    const { createU3, format } = require('u3.js/src');
    const config = require('../config');
    
    const chai = require('chai');
    require('chai')
        .use(require('chai-as-promised'))
        .should();
    
    const should = chai.should();
    const expect = chai.expect;
    const assert = chai.assert;
    
    describe('Contract\'s test cases', function() {
    
    });
    

    Write and release CarbonCoin core logic code in describe

    it('create and issue a token', async () => {
        let SYMBOL = 'CARB';
        const u3 = createU3(config);
        let account = 'ben';
        const tx = await u3.transaction(account, token => {
            token.create(account, '10000000.0000 ' + SYMBOL);
            token.issue(account, '10000000.0000 ' + SYMBOL, 'issue');
        });
    });
    

    To easily analysis the above code, we set the code for the issued Token to CARB, the issued account to ben, issued 10 million, and stored all Tokens in the distribution account ben.

    Since the smart contract code is executed asynchronously, we need to use the following code after the execution of the await u3. transaction code, waiting for the release token logic to be confirmed by the Ultrain chain, in order to truly confirm the successful execution of the code.

    //wait util it was packed in a block
    let tx_trace = await u3.getTxByTxId(tx.transaction_id);
    while (!tx_trace.irreversible) {
        await U3Utils.wait(1000);
        tx_trace = await u3.getTxByTxId(tx.transaction_id);
        if (tx_trace.irreversible) {
            console.log(tx);
            break;
        }
    }
    

    Above, the release Token code is written.

    1. Compile, deploy, and run the Energy Smart Contract

    a. Confirm that LongClaw has been launched; see previous tutorial XXXXXX

    b. Modify migrate.js

    at u3.deploy('build/MyContract', 'ben') , MyContract changed to energy;

    By looking at config.js, we can find that we have deployed the smart contract to the chain specified by chainId under the address specified by httpEndpoint through the private key of the "ben" account;

    c. Enter the terminal interface of the IDE and enter the command.

    robin build
    robin deploy
    

    The following display appears, and there is no error message, indicating that the deployment is successful.

    0_1543573990981_6196d246-6bc8-42e5-81dd-1a4766dbef47-image.png

    d. Run issueCarbonCoin.js

    0_1543574048456_ba23feb6-018a-4877-8870-fbc840383b49-image.png

    The above information appears, indicating the successful launch of CarbonCoin;

    1. Add more features to energy smart contracts

    6.1 Query account

    Add @action before the getBalance method
    0_1543574122658_f7c0399f-5104-4f79-9cb2-2ef879340384-image.png

    The @action method allows the getBalance method to be accessed by external calls.

    This method can query the number of specified Tokens owned by the specified account.

    6.2 Record the calorific value sent by the device

    The new energy device will send its heat value to the chain, and the smart contract saves the data to the database in the chain. At the same time, the smart contract sends an event notification generated by the calorific value to the registered server address;

    In this section:
    we will learn how to manipulate data on the chain;
    how to send event notifications;

    a. Introducing a new declaration in energy.ts

    import { Block } from 'ultrain-ts-lib/src/block';
    import { NAME,RNAME } from 'ultrain-ts-lib/src/account';
    import "allocator/arena";
    import { Log } from "ultrain-ts-lib/src/log";
    

    modify

    import { TransferParams, dispatchInline} from 'ultrain-ts-lib/src/action';
    

    to

    import { TransferParams, dispatchInline,Action } from 'ultrain-ts-lib/src/action';
    

    b. Create Class HeatRecord to record calorific value

    class HeatRecord implements Serializable {
        miner: string;
        timestamp: u64;
        heatValue: u64;
        primaryKey(): u64 { return NAME(this.miner) + this.timestamp; }
    
        prints(): void {
            Log.s("name = ").s(this.miner).s(", timestamp = ").i(this.timestamp).s(", heatValue = ").i(this.heatValue).flush();
        }
    }
    

    c. Add database operation declaration before “ export class CarbonToken extends Contract {”

    const tblname = "heatvalue";
    const scope = "carbon.heat";
    
    @database(HeatRecord, tblname)
    

    d. Add database operation code to class CarbonToken

    db: DBManager<HeatRecord>;
    public onInit(): void {
        this.db = new DBManager<HeatRecord>(NAME(tblname), this.receiver, NAME(scope));
    }
    public onStop(): void {
    }
    constructor(code: u64) {
        super(code);
        this._receiver = code;
        this.onInit();
    }
    

    e. Add the core code to record calorific value in class CarbonToken

    @action
    public recordHeat(quantity:u64):void{
    
        let r = new HeatRecord();
        r.miner = RNAME(Action.sender);
        r.timestamp = Block.timestamp;
        r.heatValue = quantity;
    
        let existing = this.db.exists(Action.sender+r.timestamp);
        ultrain_assert(!existing, "this record has existed in db yet.");
        r.prints();
        this.db.emplace(this.receiver,r);
    
        let value = r.heatValue+","+r.miner;
        emit("onHeatInvoked", EventObject.setString("heat",value));
    }
    

    The calorific value record mainly needs to record three data: the device account number of the sending data, the sending calorific value the sending time; for the device account, when the DApp client calls the smart contract method, it needs to be signed with the private key of the caller, so the smart contract can know who called this method, and at the same time guarantee the security of the system; we use the Action.sender method provided by the U3 framework to get the caller account. Since the account is a transferred u64 type field, we use RNAME to transfer it. Meaning string type, which is the account name;

    For the timestamp, due to the characteristics of the blockchain technology, we take the timestamp of the previous block as the record;

    We will use the account name + timestamp as the primary key of the record;

    f. Trigger the onHeatInvoked event, we inform the operator's server program through this event, and the device uploads a new calorific value record;

    import { EventObject, emit } from 'ultrain-ts-lib/src/events';
    

    Add trigger code in the recordHeat method

    emit("onHeatInvoked", EventObject.setString("heat",value));
    

    6.3 Calorific value converted to CarbonCoin

    Add the following code to energy.ts

    @action
    public exchangeCarbonCoin(from: account_name, to: account_name, quantity: Asset,memo:string): void {
        let carbonToken:Asset = quantity.divide(10);
        this.transfer(from,to,carbonToken,memo);
    }
    

    Among them, quantity.divide(10); is a simple calorific value converted to CarbonCoin's calculation formula. It can be found that the formula is recorded in the blockchain, and is open and transparent to everyone, and cannot be tampered with. And ensure the fairness of the exchange.

    Then we call the transfer method, and Ben will send the CarbonCoin to the corresponding account;

    6.4 Burning CarbonCoin to charity points

    Add a charity point object

    class ScoreRecord implements Serializable {
        name: string;
        score:u64;
        primaryKey(): u64 {return NAME(this.name)};
        prints():void{
            Log.s("name = ").s(this.name).s(",score = ").i(this.score);
        }
    }
    

    Configure the database parameters of the charity point object before energy.ts

    const tblname_s = "score";
    @database(ScoreRecord, tblname_s)
    

    Add database operation code in energy.ts

    db_s: DBManager<ScoreRecord>;
    
    
    public onInit(): void {
        this.db_s = new DBManager<ScoreRecord>(NAME(tblname_s), this.receiver, NAME(scope));
    }
    

    Add core business logic code in energy.ts

    @action
    public exchangeScore(from: account_name,to : account_name, quantity: Asset,memo:string): void {
    
        let s = new ScoreRecord();
    
        let existing = this.db_s.exists(from);
        if (existing){
            this.db_s.get(from,s);
            s.score = s.score + quantity.amount;
            this.db_s.modify(this.receiver,s);
            Log.s("thi is a edit obj");
        }else{
            s.score = quantity.amount;
            s.name = RNAME(from);
            this.db_s.emplace(this.receiver, s);
            Log.s("thi is a new obj");
        }
    
        this.transfer(from,to,quantity,memo);
    
    }
    

    The score is divided into two steps. First, we define the ratio of CarbonCoin to public interest points is 1:1. We will add the redeemed points and save them to the chain database with the name of the redemption person; then it should be consumed after redemption. CarbonCoin is transferred to an account "jack" for destruction;

    6.5 Update energy.ts smart contract

    At this point, we complete the preparation of the smart contract, and execute the command again to complete the compilation and deployment of the smart contract.

    robin build
    robin deploy
    

    posted in Development read more
  • R
    raymond

    能否解释一下产生这个区别的原因是什么?谢谢!

    posted in TS库 read more
  • R
    raymond

    By Ultrain’s Chief Cryptologist Husen Wang

    RPoS Consensus is divided into the following stages

    1、There are two committees, C1 and C2. Both can be joined freely with deposited stakes.

    2、C1 periodically generates a random number r using Verifiable Random Function. Proposers and Voters are selected by the Fisher-Yates shuffle algorithm with priorities.

    3、All proposers initiate their blocks, where all voters verify and vote upon the highest priority proposer’s block. The votes are signed with aggregatable signature. This makes lightweight client and cross chain transaction verification possible.

    4、After collecting enough votes through BFT algorithm, all nodes generate its block independently, and the corresponding proposer gets rewarded.

    Compared with DPoS

    1、RPoS is more decentralized and safe. Since DPoS has limited amount of nodes, it leaves room for “under the table” collusion between nodes, which compromises the overall security and fairness of the network. For example, during the random number generation process, fairness is required. However, nodes in DPoS can collude to predict or manipulate future random number without being detected.

    2、With larger amount of nodes, RPoS is harder to be attacked when compared with DPoS~There are a limited number of nodes in DPoS, thus easier to be attacked, which results in the overall failure of the network or a fork.

    Compared with Dfinity:

    1、The BLS signature of Dfinity needs to assume security in Gap Diffie-Hellman (GDH) group , which might limit the security level of randomness. Threshold signature requires pairing calculation that runs slowly. During the process of threshold signature, members need to exchange information, which may result in a slow threshold signature and increase vulnerability to malicious attack. RPoS limits threshold signature to voting and changes voters frequently without group setup problem.

    2、If participants can join and exit freely the group, it may lead to stolen of private key and predictable randomness.

    3、The signature of groups cannot be compared to that of the PoW system (such as bitcoin), thus the security of block confirmation cannot be guaranteed and results in a longer confirmation time.

    Compared with Algorand:

    1、Algorand selects its committee member, especially the proposers, based on probability. Which might result in multiple proposers that causes the issue of “network storm”, which increases the overall consumption of bandwidth for nodes.

    2、RPoS utilizes threshold signature algorithm, which composes a smaller voter message that is easy to transmit within the network and for client side verification.

    3、RPoS introduces an incentive mechanism for both proposer and voter, a double signature punishment system that further improves overall security.

    In addition:

    1、RPoS optimizes layer0 message transmission, to minimizes arrival time for message propagation.

    2、The RPoS plans to introduce TEE and KYC mechanism to further improve the overall system security.

    posted in Consensus read more
  • R
    raymond

    作者:Ultrain首席密码学家 王虎森

    RPoS共识,主要分成以下几个阶段:

    1、一个随机数生成委员会c1, 共识委员会c2,自由加入,但是代币抵押分别为s1和s2,因为随机数决定了共识的安全性,所以设置s1>s2。

    2、c1周期性地生成随机数r,利用Fisher–Yates shuffle算法,简称FYS(c2),依次生成两个proposer1, proposer2,优先级依次递减,然后依次生成一定数量voter。

    3、所有proposer提出块,所有voter对块进行验证,并进行投票。所有投票采用聚合签名的方式实现,这样在传递过程中可以加速到达,而且也有利于轻客户端和跨链交易验证。
    4、所有节点独立出块,对选中的proposer进行奖励。

    相对DPoS的优点:

    1、去中心化更强。因为dpos网络只有少数节点,容易导致串谋,协同作恶,导致网络的公平性和安全性出现潜在问题。比如随机数生成过程,需要很强的网络公平性,而dpos中的针对公平性的作恶是很难被发现的。

    2、被攻击的概率降低。dpos中的节点数量少,很容易被同时攻击,导致共识无法提供服务,或者分叉。

    相对Dfinity的优点:

    1、Dfinity中的bls签名需要引入GDH的额外安全假设,可能会导致随机数的安全性不够高;阈值签名中的验证需要pairing计算,速度慢;聚合签名的时候,需要组成员进行消息交换,通信延时高,导致聚合签名生成速度慢,而且容易被组内恶意成员攻击。
    2、Dfinity中群签名,假如群可以自由加入和退出的话,将导致群私钥被窃取,消息被伪造等问题。
    3、Notary的签名很难与bitcoin的pow难度相类比,所以块确认的安全性无法保证,导致确认时间可能增加。

    相对Algorand的优点:

    1、Algorand委员会成员的选择,特别是proposer的选择,是概率性的,可能带来多个proposer的网络风暴问题,节点自身的带宽消耗高;RPoS不具有该问题。

    2、RPoS采用了可聚合的签名算法,使vote消息更小,便于网络传输和客户端验证。

    3、RPoS引入了proposer和voter的激励机制,double signature惩罚机制,使安全性进一步增加。

    此外:

    1、RPoS在layer0的消息传输也进行了优化,使消息的到达时间减小,TPS增加。
    2、RPoS计划引入TEE和KYC机制,进一步保证网络的安全性。

    posted in 共识 read more
  • R
    raymond

    Thank you bingying for the reply!

    I've tried this with following code, and it worked. Thanks!

    async function test() {
    let txce = await u3.getTxTraceByTxid(txId);
    console.log(txce);
    }
    test();

    posted in Activity read more
  • R
    raymond

    Hi, guys:

    I read the hackathon questions and the first one mentioned UIP09?

    Please note: Fully understand and utilize UIP09, issue
    heterogeneous tokens, use DB, Asset, Balance components

    Would you please share a link for the UIP09 explanation ?
    I googled but no luck to get any info.

    Thanks!

    posted in Hackathon read more
  • R
    raymond

    Hi, there:

    I am reading through the develop links, it mentions that we can register a callback URL
    for event monitor:

    http://developer.ultrain.io/tutorial/contract_tutorial

    0_1538576834627_9885f1dd-3890-46b7-9bcf-da5257a94063-image.png

    The example show a local test net. Now what RPC URL I should use for the official test net that I can interact with the ultrain explorer (http://explorer.ultrain.io/)

    Looking forward your answer.

    Thanks!

    posted in Hackathon read more
  • R
    raymond

    Hi, guys:

    There is a return example in the developer site:
    0_1538575861776_7271629e-87f9-4cb0-8967-a531a32c0e38-image.png

    From the description:
    The returned message will be appended to the http response.

    I run the template/person.ts and add a get action as following:
    @action
    get(name: string): void {
    let p = new Person();
    let existing = this.db.get(NAME(name), p);
    ultrain_assert(existing, "the person does not exist.");
    Return<string>(p.name);
    Return<u32>(p.age);
    Return<u32>(p.salary);
    }

    After deploy it and then I trigger the get action in explorer. I found two requests in the developer window of chrome:

    1. Request URL: http://explorer.ultrain.io/blockchain//contract?id=leihonglu123&apiSource=client
      Request Method: GET

    Here is the response:

    {"state":"success","data":{"_id":"5bb337d1d13eb773585a8cf8","name":"leihonglu123","createdAt":"2018-10-02T09:18:09.446Z","abi":{"version":"ultraio:1.0","types":[{"new_type_name":"u32","type":"uint32"}],"structs":[{"name":"add","base":"","fields":[{"name":"name","type":"string"},{"name":"age","type":"u32"},{"name":"salary","type":"u32"}]},{"name":"modify","base":"","fields":[{"name":"name","type":"string"},{"name":"salary","type":"u32"}]},{"name":"remove","base":"","fields":[{"name":"name","type":"string"}]},{"name":"get","base":"","fields":[{"name":"name","type":"string"}]},{"name":"Person","base":"","fields":[{"name":"name","type":"string"},{"name":"age","type":"u32"},{"name":"salary","type":"u32"}]}],"actions":[{"name":"add","type":"add","ricardian_contract":""},{"name":"modify","type":"modify","ricardian_contract":""},{"name":"remove","type":"remove","ricardian_contract":""},{"name":"get","type":"get","ricardian_contract":""}],"tables":[{"name":"humans","index_type":"i64","key_names":[],"key_types":[],"type":"Person"}],"ricardian_clauses":[],"error_messages":[],"abi_extensions":[]},"updatedAt":"2018-10-03T13:52:35.648Z","id":"5bb337d1d13eb773585a8cf8"}}

    Request URL: http://explorer.ultrain.io/blockchain//contractInteract
    Request Method: POST

    with the following data:

    {"contractName":"leihonglu123","actionName":"get","params":{"name":"leihonglu"},"privateKey":"5Jb8jdakgMe8LHggPwRQdVDXtU7KuBWE6shYKbfwvU3juNMggp8","accountName":"leihonglu123"}

    Here is the response:

    {"state":"success","data":{"transaction_id":"f0926fc766389a0070ececdb992574b2fbcbc47ddd706f4bb27386173e1baeca","broadcast":true,"transaction":{"compression":"none","transaction":{"expiration":"2018-10-03T15:07:00","ref_block_num":45651,"ref_block_prefix":1057728309,"net_usage_words":0,"max_cpu_usage_ms":0,"delay_sec":0,"context_free_actions":[],"actions":[{"account":"leihonglu123","name":"get","authorization":[{"actor":"leihonglu123","permission":"active"}],"data":"096c6569686f6e676c75"}],"transaction_extensions":[]},"signatures":["SIG_K1_Kde1CY7zgyHKT7XKJN35CWw8kA3aNq4aGbKkPcVhbjE4TrJTjg7pk7nRYJeYsY7wtaScqaxpdtAYeqY9xiEMY62BC3hHuT"]}}}

    But where is the "get" action return value? Thanks!

    posted in Activity read more