Save
Saving
  • B
    benyasin

    <img src="https://user-images.githubusercontent.com/1866848/50449130-2e4fc180-0960-11e9-8878-2fc1cb817593.jpg" width="300" align=center />

    Windows下DApp开发攻略

    由于有界面的本地链环境longclaw在windows下存在未解的兼容性问题,考虑到部分Windows用户的开发需求,
    本文给出另外两种可行的开发方案

    一、基于无界面的docker链环境开发

    本地链环境依赖docker,下文以windows 10下docker安装为例,其它版本的windows请自行攻略

    1、安装、配置并启动docker

    <img src="https://user-images.githubusercontent.com/1866848/50449344-c0a49500-0961-11e9-98f2-c7ee7dd08600.png" width="300" align=center />

    • 注意启动的时候可能会遇到内存不够的情况,需要找到右下角的鲸鱼图标右键 - 设置,修改VM的内存大小后Docker会重新启动

    <img src="https://user-images.githubusercontent.com/1866848/50449387-11b48900-0962-11e9-885f-5f4334a42ae3.png" width="300" align=center />

    <img src="https://user-images.githubusercontent.com/1866848/50449475-9acbc000-0962-11e9-9a3b-2c0a35b15f2b.png" width="300" align=center />

    2、下载镜像并构建链环境

    <img src="https://user-images.githubusercontent.com/1866848/50449562-23e2f700-0963-11e9-940d-6d120f2900c2.png" width="300" align=center />

    • 使用默认创建的固定测试账号开发,账号名和公私钥在 u3.js/src/scratch目录下面

    <img src="https://user-images.githubusercontent.com/1866848/50449624-71f7fa80-0963-11e9-892e-8d102724fbcd.png" width="300" align=center />

    二、连接远程测试网络进行开发

    如果不想构建本地链环境,也可以直接基于远程测试网络进行开发

    1、在浏览器https://explorer.ultrain.io/account-new或UltrainOneIOS版Android版的钱包中创建一个测试账号,并保存好私钥与助记词

    2、在浏览器中为你的账号申请资源https://developer.ultrain.io/resources

    <img src="https://user-images.githubusercontent.com/1866848/50450097-94d7de00-0966-11e9-9eb1-3d45cce9ed93.png" width="300" align=center />

    3、修改Robin产生的项目中config.js配置信息

    const config = {
      httpEndpoint: 'http://benyasin.s1.natapp.cc',
      httpEndpoint_history: 'http://history.natapp1.cc',
      broadcast: true,
      debug: false,
      verbose: false,
      sign: true,
      logger: {
        log: console.log,
        error: console.error,
        debug: console.log
      },
      chainId: '262ba309c51d91e8c13a7b4bb1b8d25906135317b09805f61fcdf4e044cd71e8',
      keyProvider: '改为你申请的测试网账号的私钥',
      binaryen: require('binaryen')
    };
    module.exports = config;
    
    

    同时不要忘了修改migrate.js中的账号为你申请的测试网账号名

    三、本地进行合约或DApp开发

    具体开发流程请使用Robin框架,参考上面几篇教程或以下几个示例:

    posted in Longclaw read more
  • B
    benyasin

    <img src="https://user-images.githubusercontent.com/1866848/46092827-535d5880-c1e8-11e8-8a65-f5d9d74df96e.png" width="250" align=center />

    Javascript封装的负责与链交互的通用库

    应用环境

    浏览器(ES6)或 NodeJS

    如果你想集成u3.js到react native环境中,有一个可行的方法,借助rn-nodeify实现,参考示例U3RNDemo

    使用方法

    一、如果是在浏览器中使用u3,请参考以下用法:

        <!DOCTYPE html>
        <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>test</title>
                <script src="../dist/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:'0eaaff4003d4e08a541332c62827c0ac5d96766c712316afe7ade6f99b8d70fe',
                    symbol: 'UGAS'
                  });
            
                  u3.getChainInfo((err, info) => {
                    if (err) {throw err;}
                    console.log(info);
                  });
                </script>
            </head>
            <body>
            </body>
        </html>
    

    二、 如果是在NodeJS环境中使用u3,请参照以下用法:

    • 安装u3

    npm install u3.jsyarn add u3.js

    • 初始化

    const { createU3 } = require('u3.js/src');
    let config = {
      httpEndpoint: 'http://127.0.0.1:8888',
      httpEndpoint_history: 'http://127.0.0.1:3000',
      chainId: '0eaaff4003d4e08a541332c62827c0ac5d96766c712316afe7ade6f99b8d70fe',
      keyProvider: ['PrivateKeys...'],
      broadcast: true,
      sign: true
    }
    let u3 = createU3(config);
    
    u3.getChainInfo((err, info) => {
      if (err) {throw err;}
      console.log(info);
    });
    
    
    • 本地环境运行

    本地运行u3,需要依赖docker.

    1.从这里 下载docker并安装;

    2.然后添加中国区镜像地址:https://registry.docker-cn.com;

    3.点击"Apply & Restart";

    <img src="https://user-images.githubusercontent.com/1866848/46121838-3d7f8000-c248-11e8-933a-fbcf30cfc443.png" width="500" hegiht="700" align=center />

    4.进入u3.js/docker && ./start.sh

    配置

    全局配置

    • <b>httpEndpoint</b> string - 链实时API的http或https地址.如果是在浏览器环境中使用u3,请注意配置相同的域名.
    • <b>httpEndpoint_history</b> string - 链历史API的http或https地址.如果是在浏览器环境中使用u3,请注意配置相同的域名.
    • <b>chainId</b> 链唯一的ID. 链ID可以通过 [httpEndpoint]/v1/chain/get_chain_info获得.
    • <b>keyProvider</b> [array<string>|string|function] - 提供私钥用来签名交易. 提供用于签名事务的私钥。
      如果提供了多个私钥,不能确定使用哪个私钥,可以使用调用get_required_keysAPI 获取要使用签名的密钥. 如果是函数,那么每一个交易都将会使用该函数.
      如果这里不提供keyProvider,那么它可能会Options配置项提供在每一个action或每一个transaction中
    • <b>expireInSeconds</b> number - 事务到期前的秒数,时间基于nodultrain的时间.
    • <b>broadcast</b> [boolean=true] - 默认是true。使用true将交易发布到区块链,使用false将获取签名的事务.
    • <b>verbose</b> [boolean=false] - 默认是false。详细日志记录.
    • <b>debug</b> [boolean=false] - 默认是false。低级调试日志记录.
    • <b>sign</b> [boolean=true] - 默认是true。使用私钥签名交易。保留未签名的交易避免了提供私钥的需要.
    • <b>logger</b> - 默认日志配置.
    logger: {
      log: config.verbose ? console.log : null,  // 如果值为null,则禁用日志
      error: config.verbose ? console.error : null,
    }
    

    Options配置项

    Options可以在方法参数之后添加. Authorization应用于单独的actions.比如:

    options = {
      authorization: '[email protected]',
      broadcast: true,
      sign: true
    }
    
    
    u3.transfer('alice', 'bob', '1.0000 UGAS', '', options)
    
    • <b>authorization</b> [array<auth>|auth] - 指明账号和权限,典型地应用于多重签名的配置中. Authorization必须是一个字符串格式,形如[email protected]
    • <b>broadcast</b> [boolean=true] - 默认是true。使用true将交易发布到区块链,使用false将获取签名的事务.
    • <b>sign</b> [boolean=true] - 默认是true。使用私钥签名交易。保留未签名的交易避免了提供私钥的需要.
    • <b>keyProvider</b> [array<string>|string|function] - 就像global配置项中的keyProvider一样,这里的配置可以以覆盖全局配置的形式为每一个action或每一个transaction提供单独的私钥.
    await u3.anyAction('args', {keyProvider})
    await u3.transaction(tr => { tr.anyAction() }, {keyProvider})
    

    创建账号

    创建账号需要花费creator账号的一些代币,为新账号抵押部分RAM和带宽

    const u3 = createU3(config);
    const name = 'abcdefg12345';//普通账号需要满足规则:必须为12345abcdefghijklmnopqrstuvwxyz中的12位
    let params = {
       creator: 'ben',
       name: name,
       owner: pubkey,
       active: pubkey,
       updateable: 1,//可选,账号是否可以更新(更新合约)
    };
    await u3.createUser(params);
     
    

    转账(UGAS)

    转账方法使用非常频繁,UGAS的转账需要调用系统合约utrio.token.

    const u3 = createU3(config);
    const c = await u3.contract('utrio.token')
    
    // 使用位置参数
    await c.transfer('ben', 'bob', '1.2000 UGAS', '')
    // 使用名称参数
    await c.transfer({from: 'bob', to: 'ben', quantity: '1.3000 UGAS', memo: ''})
    

    签名

    使用 { sign: false, broadcast: false } 创建一个u3实例并且做一些action, 然后将未签名的交易发送到钱包中.

      const u3_offline = createU3({ sign: false, broadcast: false });
      const c = u3_offline.contract('utrio.token');
      let unsigned_transaction = await c.transfer('ultrainio', 'ben', '1 UGAS', 'uu');
    

    在钱包中你可以提供私钥或助记词来签名,并将签名后的交易发送到链上.

      const u3_online = createU3();
      let signature = await u3_online.sign(unsigned_transaction, privateKeyOrMnemonic, chainId);
      if (signature) {
         let signedTransaction = Object.assign({}, unsigned_transaction.transaction, { signatures: [signature] });
         let processedTransaction = await u3_online.pushTx(signedTransaction);
      }
    
    

    资源

    调用合约只会消耗合约Owner的资源,所以如果你想部署一个合约,请先购买一些资源.

    • resourcelease(payer,receiver,slot,days)
    const u3 = createU3(config);
    const c = await u3.contract('ultrainio')
    
    await c.resourcelease('ben', 'bob', 1, 10);// 1 slot for 10 days
    
    

    通过以下方法查询资源详情.

    const resource = await u3.queryResource('abcdefg12345');
    console.log(resource)
    
    

    合约

    部署合约

    部署合约需要提供包含目标文件为 .abi,.wast,*.wasm 的三个文件的文件夹.

    • deploy(contracts_files_path, deploy_account) 第一个参数为合约目标文件的绝对路径,第二个合约部署者账号.
      const u3 = createU3(config);
      await u3.deploy(path.resolve(__dirname, '../contracts/token/token'), 'bob');
    
    

    调用合约

    const u3 = createU3(config);
    const c = await u3.contract('ben');
    await c.transfer('bob', 'ben', '1.0000 UGAS','');
    
    //或者像这样调用
    await u3.contract('ben').then(sm => sm.transfer('bob', 'ben', '1.0000 UGAS',''))
    
    // 一笔交易也可以包含多个合约中的多个action
    await u3.transaction(['ben', 'bob'], ({sm1, sm2}) => {
       sm1.myaction(..)
       sm2.myaction(..)
    })
    

    发行代币

    const u3 = createU3(config);
    const account = 'bob';
    await u3.transaction(account, token => {
        token.create(account, '10000000.0000 DDD');
        token.issue(account, '10000000.0000 DDD', 'issue');
    });
    
    const balance = await u3.getCurrencyBalance(account, account, 'DDD')
    console.log('currency balance', balance)
    

    事件

    Ultrain提供了一个事件注册监听机制用来解决异步场景下业务需求.客户端首先订阅一个事件,提供一个用来接收消息的地址,
    当合约中的某个方法触发时,该地址会收到来自链的推送消息.

    订阅/取消订阅

    • registerEvent(deployer, listen_url)
    • unregisterEvent(deployer, listen_url)

    deployer : 合约的部署者账号

    listen_url : 接收消息的地址

    注意: 如果你是在本地docker环境中使用改机制,请确认接收地址是一个可以从docker访问到的本地宿主地址.

    const u3 = createU3(config);
    const subscribe = await u3.registerEvent('ben', 'http://192.168.1.5:3002');
    
    //or
    const unsubscribe = await u3.unregisterEvent('ben', 'http://192.168.1.5:3002');
    

    监听

    const { createU3, listener } = require('u3.js/src');
    listener(function(data) {
       // do callback logic
       console.log(data);
    });
    
    U3Utils.test.wait(2000);
    
    //must call listener function before emit event
    const contract = await u3.contract(account);
    contract.hi('ben', 30, 'It is a test', { authorization: [`[email protected]`] });
    
    

    posted in U3.js read more
  • B
    benyasin

    Ultrain共建时代来了, 贡献力量吧~

    posted in 共识 read more
  • B
    benyasin

    具体多少不好说,但我觉得收益一定要稳健,如果像比特币一样,前期收益巨大,越到后面越小,随着个人挖矿的不再现实,到最后连矿场也逐渐快要付不起电费,这必然打击了广大投资者信心,而如果能保证后面投资的人不会因为人多太收益减少,那自然会吸引更多的人来投资建设

    posted in 经济模型 read more
  • B
    benyasin

    本篇教程将指导开发者基于一个真实的商业场景,采用Ultrain的Robin Framework,编写一个DApp,在这个过程中您可以深入了解Ultrain在DApp开发方面的技术特点,快速掌握如何编写一个DApp,您会发现基于Robin Framework编写DApp是一件非常轻松的事情。本教程的所有源代码都可以在github中找到:https://github.com/ultrain-os/UltrainDappDemo

    本教程属于中级教程,通过本教程,你可以学习DApp开发的基本概念,Ultrain Database和触发器的用法;

    前提假设:

    1. 您已经学习过“快速编写XXXXX”教程,并通过该教程的指引,已经安装了Ultrain Robin开发环境,本地测试环境LongClaw;

    系统要求:

    1. Mac操作系统(最佳体验)或Linux、Windows系统;
    2. IDE:WebStorm
    3. Docker

    业务场景介绍:

    NewEnergy公司生产的新能源设备,通过使用新的清洁能源,可以极大的降低碳排放,NewEnergy公司希望将其降低的碳排放量转换为相应的碳币,一方面使用NewEnergy公司设备的公司,通过该碳币可以获取额外收益;另一方面,碳排放较多的公司,可以通过购买碳币,为减排提供资金支持,提升企业在碳排放方面的公益形象;所以,该场景中存在如下的角色:

    • NewEnergy公司:碳币的运营方,创造并发行碳币,NewEnergy使用的Ultrain账户为ben;
    • B公司:使用NewEnergy公司生产的新能源设备B,在使用设备的过程中,其消耗的热量值每30秒会纪录在区块链上,并由该值转换为碳币发放到B公司的Ultrain账户,tom;
    • C公司:航空公司,化石能源使用大户,通过购买并消耗碳币,提升企业公益形象;C公司使用的Ultrain账户为tony
    • D公司:汽车制造公司,化石能源使用大户,通过购买并消耗碳币,提升企业公益形象;D公司使用的Ultrain账户为jerry
    • Ultrain账户jack,为兑换公益积分时,CarbonCoin燃烧掉打入的地址,打入该地址的CarbonCoin将不能再被使用;

    程序架构介绍:

    0_1541589204993_a8207102-77c7-45e5-b11b-62697930e625-image.png

    如上图所示,系统由4个程序构成:

    1. Energy智能合约:运行在Ultrain链上的智能合约,基于TypeScript开发;
    2. MiningDApp:采集节能量数据的终端DApp,基于U3框架开发;
    3. UDApp:兑换公益积分DApp,基于U3框架开发
    4. EnergyServer:监听并执行兑换的服务器程序,基于NodeJS框架开发;

    Step1: Energy智能合约开发

    1. 建立项目目录,初始化项目环境

    mkdir CarbonProject
    cd CarbonProject
    robin init

    2. 使用Javascript开发IDE WebStorm,打开项目CarbonProject

    0_1541589483260_80821be6-aa38-4c59-bf72-07db56d79518-image.png
    简单介绍一下项目中各目录的功能:

    contract : 项目的智能合约目录,该目录下放置项目的主智能合约,每个项目中只有一个智能合约被编译,就是该目录下的智能合约;同时,Ultrain现在每个账户只支持部署一个智能合约;
    migrations:智能合约部署参数的配置文件
    node_modules:项目依赖的JS类库
    template:用于示例的智能合约代码
    test: 测试代码目录,测试代码都是javascript代码;
    config.js:U3框架访问Ultrain链时,需要配置的默认参数;这些参数也可以在U3框架创建时动态赋值;

    3. 编写Energy智能合约

    a. 删除contract目录下的MyContract.ts文件,将template/token/token.ts文件拷贝到contract目录下,修改文件名为 energy.ts;修改类名为 energy; 需要注意的是:发行代币的合约需要遵循UIP的规范,本例中需要实现UIP06的接口,相关规范文档请参考:https://developer.ultrain.io/tutorial/UIP_Introduce.
    b.编写调用energy.ts,发行CarbonCoin的代码。

    • 在test目录下,建立一个新的javascript文件,命名为issueCarbonCoin.js
      In the test directory, create a new javascript file named issueCarbonCoin.js
    • 建立代码框架
    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() {
    
    });
    
    • 在describe中编写发行CarbonCoin核心逻辑代码
    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');
        });
    });
    

    简单解析上述代码,我们设定了发行的Token的代码为CARB,发行的账户为ben,发行了1000万枚,并将所有Token存储到发行账户ben中。

    • 由于智能合约代码是异步执行的,我们需要在await u3.transaction 代码执行后,采用下述代码,等待发行Token逻辑被Ultrain链确认,才能真正确认代码执行成功
    //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;
        }
    }
    

    以上,发行Token代码编写完成。

    4. 编译、部署并运行Energy智能合约

    a. 确认LongClaw已经启动;参见之前教程XXXXXX;
    b. 修改migrate.js

    u3.deploy('build/MyContract', 'ben')中,MyContract修改为energy
    at u3.deploy('build/MyContract', 'ben') , MyContract changed to energy;
    通过查看config.js可以发现,我们通过“ben”账户的私钥,将该智能合约部署到httpEndpoint指定的地址下的chainId指定的链上;

    c. 进入IDE的terminal界面,输入命令

    robin build
    robin deploy

    出现如下显示,并没有错误提示,表示部署成功
    0_1541589822287_fd9f17a6-6eee-4a1c-b433-490392946a47-image.png
    d. 运行issueCarbonCoin.js
    0_1541590045095_c0530a28-48b0-4ebb-8a77-6f3e2bcdb56e-image.png
    出现以上信息,说明CarbonCoin发行成功;

    5.为 energy智能合约添加更多功能

    5.1 查询账户

    在getBalance方法前添加@action
    @action方法让getBalance方法可以被外部调用访问;
    该方法可以查询指定账户拥有的指定Token的数量

    5.2 记录设备发送的热量值

    新能源设备会将其产生的热量值发送到链上,智能合约将该数据保存到链上的数据库中,同时,智能合约向注册的服务器地址发送热量值产生的事件通知;

    在本节,我们将学习如何操作链上数据;如何发送事件通知;
    a. 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";
    

    修改之前引入代码

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

    b. 建立HeatRecord类,用于记录热量值

    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. 在export class CarbonToken extends Contract { 前添加数据库操作申明

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

    d. 在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. 在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));
    }
    

    热量值记录主要需要记录三个数据,发送数据的设备账号,发送的热量值以及发送时间;针对设备账号,DApp客户端调用智能合约的方法时,需要用调用者的私钥进行签名,所以智能合约可以知道是谁调用的本方法,同时能保证系统的安全;我们使用U3框架提供的Action.sender方法得到调用者账号,由于该账号是一个转移后的u64类型字段,我们用RNAME将其转义为string类型,即账号名;

    针对时间戳,由于区块链技术的特点,我们取上个区块的时间戳作为记录;

    我们将 账号名+时间戳 作为记录的主键;

    f. 触发onHeatInvoked事件,我们通过该事件告知运营方的服务器程序,有设备上传新的热量值记录了;

    引入类:

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

    在recordHeat方法中添加触发器代码

    emit("onHeatInvoked", EventObject.setString("heat",value));
    
    5.3 热量值兑换CarbonCoin

    在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);
    }
    

    其中quantity.divide(10);是一个简单的热量值兑换CarbonCoin的计算公式,可以发现,该公式是记录在区块链中,对所有人都是公开和透明的,并且不能篡改,保证兑换的公平性;

    然后我们调用transfer方法,由ben将兑换的CarbonCoin发给相应的账户;

    5.4 燃烧CarbonCoin兑换公益积分

    添加公益积分对象;

    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);
        }
    }
    

    在energy.ts前配置公益积分对象的数据库参数

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

    在energy.ts内添加数据库操作代码

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

    在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);
    
    }
    

    对积分分为两步操作,首先我们定义CarbonCoin兑换公益积分的比例是1:1,我们将兑换到的积分累加起来,用兑换人name作为主键保存到链上数据库;然后将兑换后应该消耗掉的CarbonCoin转移到一个账户“jack”,用于销毁;

    6. 更新energy.ts智能合约

    至此,我们完成了智能合约的编写工作,再次执行命令完成对智能合约的编译和部署
    robin build
    robin deploy

    Step2:DApp编写

    本教程有两个DApp需要编写,一个是MiningDApp,一个是UDApp,我们采用基于Javascript语言的U3框架编写DApp,为简化,我们将这两个DApp包含在一个Project中;

    1. 建立DApp项目

    在WebStorm中,新建项目->Node.js Express App->Template选择Pug(Jade);项目名称修改为CarbonDApp;点击‘Create’创建项目
    0_1541590414902_6f675a81-1b07-4084-bc40-686d7f5bd9c0-image.png

    2. 导入项目依赖类库

    将CarbonProject中node_modules中所有目录拷贝到CarbonDApp的node_modules目录下,重复目录跳过不用拷贝;

    3. 修改DApp服务器端口

    由于默认的3000端口已经被占用,需要修改DApp的服务器端口;
    打开bin/www 文件,找到var port = normalizePort(process.env.PORT || '3000');将3000修改为3001

    4. 建立账户信息浏览页面

    在public目录下建立index.html页面,添加如下代码到页面:

    <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>
    

    5.启动DApp服务,测试页面

    在npm界面,双击start按钮

    0_1541590527389_eb30fbdd-9a06-4871-9545-330f571a0774-image.png

    服务器启动成功后,访问http://127.0.0.1:3001/index.html,出现如下页面:
    可以看到,发行方ben账户中持有发行的所有CARB Token 1000万枚;其他账户还没有持有CARB Token;

    0_1541590587092_bca3f250-044c-4da7-8f0f-fff1dddc7f29-image.png

    5. 建立mining.html页面

    mining.html页面,节能设备1和节能设备2每30秒将前30秒产生的热量值发送给energy智能合约,兑换CarbonCoin;
    在public目录下建立mining.html页面,输入如下代码:

    <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>
    

    访问http://127.0.0.1:3001/mining.html,出现如下页面:
    0_1541590651229_36d0df6d-2c55-4377-bced-0469502b2e0b-image.png

    这里需要注意两点,

    • 给energy智能合约发送数据时,需要持有该账户的私钥才可以签名并发送,这里为了简化编程将私钥直接写到了网页上,实际程序中需要对私钥进行保护和隐藏;
    • 为简化编写,bob和tom的DApp程序直接写到了一个页面中,实际应该是两个DApp程序实例,分别运行中两个设备上;

    进展到这里,我们的节能设备已经可以发送数据给智能合约了,而energy智能合约接受到数据后,会将数据传递给运营商服务器,由运营商服务器再次调用energy智能合约,兑换carboncoin,为此,我们需要建立EnergyServer;

    Step3: EnergyServer

    1. 建立事件监听并处理事件

    本节,我们来处理上面的热量值数据上传事件

    1.1 建立监听事件服务器代码

    在CarbonDApp项目中bin目录下,新建一个文件msgBroker.js,输入如下代码:

    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;
            }
        }
    }
    

    该代码在3002端口建立了一个服务,在节能设备上传其热量值后,智能合约会给该服务发送哪台节能设备上传了热量值数据,该服务调用energy智能合约的exchangeCarbonCoin方法进行CarbonCoin兑换。

    1.2 建立监听事件服务器启动脚本

    打开CarbonDApp/package.json文件,在scripts属性中添加如下代码:"event": "node ./bin/msgBroker"

    1.3 启动监听服务器

    刷新npm窗口,出现‘event’选项
    双击event,完成服务器启动
    0_1541590746148_9916a209-c517-447f-aca9-faec2536f94b-image.png

    0_1541590782407_f774f5ce-6fb8-4a80-8b0a-8b4d110445c5-image.png

    1.4 在链上注册事件监听机制

    通过ifconfig命令,查询本机内网地址,比如:10.20.8.32
    在CarbonProject/test目录下,建立registerEvent.js文件;在文件中输入如下代码:

    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);
        });
    });
    

    注意修改注册地址为本机的内网地址,并且不能使用127.0.0.1。
    运行event register测试用例,完成事件注册;

    0_1541590854785_4f9481ac-04bb-4b47-a6c7-38b5b68c21cf-image.png

    1.5 测试热量值兑换CarbonCoin功能

    访问http://127.0.0.1:3001/mining.html页面,该页面每30秒,bob和tom两个账户都会随机发送15000到20000之间的一个随机值作为热量值给智能合约兑换CarbonCoin;
    访问并刷新http://127.0.0.1:3001/index.html页面,可以发现bob和tom的CarbonCoin账户每30秒都在增加;

    Step4: 完善DApp功能

    1. 添加转账功能

    节能设备获取到CarbonCoin后,可以将CarbonCoin出售给企业,比如航空公司等获利,简单体现就是设备账户可以转账给企业公司账户;

    打开/CarbonDApp/index.html页面,添加代码

    1.1 建立转账时使用的u3对象
    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 添加转账的js代码,用于调用相应的智能合约方法
    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 在页面添加对sendcoin()方法的调用,更新如下代码:
    <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 访问页面并测试功能

    访问:http://127.0.0.1:3001/index.html,点击“给航空公司C转账”按钮,等待一段时间后(最长约10秒,为Ultrain交易确认时间),出现如下提示,表示转账成功。
    0_1541590953725_ff8eddb5-76a5-472d-b7a7-e4a7ecedb0fb-image.png

    2.添加企业公司将CarbonCoin兑换为公益积分功能

    企业公司通过将CarbonCoin兑换为公益积分,对降低碳排放作出自己的贡献,提升企业声誉:

    打开/CarbonDApp/index.html页面,添加代码

    2.1 建立积分兑换时使用的u3对象
    let u3_tony = U3.createU3({
        keyProvider:'5KbHvFfDXovPvo2ACNd23yAE9kJF7Mxaws7srp6VapjMr7TrHZB',//tony's private key
    })
    
    let u3_jerry = U3.createU3({
        keyProvider:'5JFz7EbcsCNHrDLuf9VpHtnLdepL4CcAEXu7AtSUYfcoiszursr',//jerry's private key
    })
    
    2.2 添加兑换的js代码,用于调用相应的智能合约方法
    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 在页面添加对exchangeScore()方法的调用,更新如下代码:
    <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 访问页面并测试功能

    访问:http://127.0.0.1:3001/index.html 点击“购买公益积分”按钮,等待一段时间后(最长约10秒,为Ultrain交易确认时间),出现如下提示,表示积分兑换成功,同时可以看到页面最下方的CarbonCoin销毁账户中,销毁的CarbonCoin数量增多了。
    0_1541591037743_c6e6a2c4-a117-4172-854c-a67a3ecea04d-image.png

    3.查询公益积分兑换历史

    通过查询公益积分的兑换历史,我们可以建立一个企业公益积分的排行榜

    3.1 建立页面

    在CarbonDApp/public/目录中,新建leaderboard.html,输入如下代码:

    <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>
    

    我们通过

    u3.getAllTxs(1,10000000,{"actions.name":"exchangeScore"},{_id:-1});
    

    方法,查询到所有积分兑换的交易,将其具体数据打印到页面。

    3.2 运行页面

    访问http://127.0.0.1:3001/leaderboard.html,可以看到如下页面:
    0_1541591082327_8036739d-a11f-479f-8a7f-f71a0d177816-image.png
    这里展示了所有积分兑换的历史记录。

    总结

    本教程系统的介绍了如何编写一个端到端的网页版DApp应用,虽然在编写的过程中做了相应的化简,但总体来说描绘了DApp应用开发的全貌,开发者可以做这个框架的基础上,丰富并完善自己的DApp应用。
    关于在iOS和Android上开发DApp,可以将u3.js整合到相应的混合Native开发框架中实现,其原理与网页版DApp一致。u3.deploy('build/MyContract', 'ben')中,MyContract修改为energy
    at u3.deploy('build/MyContract', 'ben') , MyContract changed to energy;
    通过查看config.js可以发现,我们通过“ben”账户的私钥,将该智能合约部署到httpEndpoint指定的地址下的chainId指定的链上;

    posted in 开发 read more
  • B
    benyasin

    目前已经支持多平台了: 兼容windows, linux,赞~

    posted in Longclaw read more